diff options
| -rw-r--r-- | assets/session.css | 6 | ||||
| -rw-r--r-- | assets/session.js | 46 | ||||
| -rw-r--r-- | src/main.rs | 5 | ||||
| -rw-r--r-- | src/play.rs | 2 | ||||
| -rw-r--r-- | src/session.rs | 2 | ||||
| -rw-r--r-- | templates/session.html | 3 |
6 files changed, 61 insertions, 3 deletions
diff --git a/assets/session.css b/assets/session.css new file mode 100644 index 0000000..03db59d --- /dev/null +++ b/assets/session.css @@ -0,0 +1,6 @@ +#hand { + display: flex; + flex-direction: row; + justify-content: center; + flex-wrap: wrap; +}
\ No newline at end of file diff --git a/assets/session.js b/assets/session.js index 981cff1..3f17ad0 100644 --- a/assets/session.js +++ b/assets/session.js @@ -1,4 +1,5 @@ const colorSelect = document.getElementById('color-select'); +const hand = document.getElementById('hand'); const id = document.getElementById('session-script').dataset.id; const websocket = new WebSocket(`/session/${id}/play`); @@ -28,6 +29,49 @@ websocket.addEventListener('message', (event) => { } else if (Object.hasOwn(message, 'Hand')) { const payload = message.Hand; colorSelect.disabled = false; + + Array.from(hand.children).forEach((child) => { + child.remove(); + }) + + payload.forEach((handObject) => { + if (Object.hasOwn(handObject, 'CustomDeck')) { + const customDeck = handObject.CustomDeck; + + /* + const cardWidthRatio = 100 / customDeck.width; + const cardHeightRatio = 100 / customDeck.height; + const cardHorizontalOffset = cardWidthRatio * (customDeck.card_id % customDeck.width); + const cardVerticalOffset = cardHeightRatio * Math.floor(customDeck.card_id / customDeck.width); + + const card = document.createElement('img'); + card.src = customDeck.face; + card.style.clipPath = `rect(${cardVerticalOffset}% ${cardWidthRatio}% ${cardVerticalOffset + cardHeightRatio}% ${cardHorizontalOffset}%`; + card.style.objectFit = 'none'; + */ + + // 214x334 + const cardBounds = document.createElement('div'); + cardBounds.style.overflow = 'hidden'; + const cardFace = document.createElement('img'); + cardFace.src = customDeck.face; + cardFace.addEventListener('load', () => { + const width = cardFace.naturalWidth / customDeck.width; + const height = cardFace.naturalHeight / customDeck.height; + const offsetX = width * (customDeck.card_id % customDeck.width); + const offsetY = height * Math.floor(customDeck.card_id / customDeck.width); + + cardBounds.style.width = `${width}px`; + cardBounds.style.height = `${height}px`; + cardBounds.style.scale = '0.8'; + + cardFace.style.marginLeft = `-${offsetX}px`; + cardFace.style.marginTop = `-${offsetY}px`; + }); + cardBounds.appendChild(cardFace); + hand.appendChild(cardBounds); + } + }); } }); @@ -37,7 +81,7 @@ websocket.addEventListener('close', () => { colorSelect.addEventListener('change', () => { if (colorSelect.value === '') return; - + colorSelect.disabled = true; websocket.send(JSON.stringify({ diff --git a/src/main.rs b/src/main.rs index d9852e3..fc0a7bb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -65,6 +65,7 @@ async fn serve_static(Path(path): Path<String>) -> Response { Some(content) => { let mime = match path.split('.').next_back() { Some("js") => "application/javascript", + Some("css") => "text/css", _ => "application/octet-stream", }; ([(header::CONTENT_TYPE, mime)], content.data).into_response() @@ -73,7 +74,9 @@ async fn serve_static(Path(path): Path<String>) -> Response { } } -async fn find_session(Query(query): Query<HashMap<String, String>>) -> axum::response::Result<Redirect> { +async fn find_session( + Query(query): Query<HashMap<String, String>>, +) -> axum::response::Result<Redirect> { let id = query.get("id").ok_or(StatusCode::NOT_FOUND)?; Ok(Redirect::to(format!("/session/{}", id).as_str())) } diff --git a/src/play.rs b/src/play.rs index 574e8b8..e08a895 100644 --- a/src/play.rs +++ b/src/play.rs @@ -34,7 +34,7 @@ pub async fn handle_play(mut socket: WebSocket, app_state: Arc<AppState>) { // Blocked so that the guard is dropped after cloning the color names, // preventing a potential deadlock when using .await after sending something // through the socket, which would be possible if using tokio::sync::Mutex. - // Of course, the sessions HashMap is wrapped instd::sync types instead, which + // Of course, the sessions HashMap is wrapped in std::sync types instead, which // does not enable locking the mutex through .await anyway. let colors: Vec<String> = { let sessions = app_state.sessions.read().unwrap(); diff --git a/src/session.rs b/src/session.rs index 6aeee9f..e472adf 100644 --- a/src/session.rs +++ b/src/session.rs @@ -36,6 +36,8 @@ pub struct CustomDeck { /// Whether the card back should be used as the hidden image (instead of the last slot of the /// `face` image). back_is_hidden: bool, + /// ID of the custom card within the deck. + card_id: f64, } impl Session { diff --git a/templates/session.html b/templates/session.html index 122c80b..d0b6ddd 100644 --- a/templates/session.html +++ b/templates/session.html @@ -4,6 +4,7 @@ <meta charset="UTF-8"> <title>{{session.steam_name}}'s game</title> <script id="session-script" data-id="{{id}}" src="/dist/session.js" defer></script> + <link href="/dist/session.css" rel="stylesheet" type="text/css"> </head> <body> <h1>{{session.steam_name}}'s game</h1> @@ -15,5 +16,7 @@ <hr> </select> </form> +<h2>Hand</h2> +<div id="hand"></div> </body> </html>
\ No newline at end of file |
