Do a lot of refinements to be able to edit and then update a card
All checks were successful
Monorepo build / build-flake (push) Successful in 3s
All checks were successful
Monorepo build / build-flake (push) Successful in 3s
This commit is contained in:
parent
d02612e410
commit
33a4f2f46a
@ -1,4 +1,4 @@
|
||||
[toolchain]
|
||||
channel = "1.86.0"
|
||||
targets = [ "wasm32-unknown-unknown", "thumbv6m-none-eabi" ]
|
||||
components = [ "rustfmt", "rust-analyzer", "clippy" ]
|
||||
targets = [ "x86_64-unknown-linux-gnu", "wasm32-unknown-unknown", "thumbv6m-none-eabi" ]
|
||||
components = [ "cargo", "rustc", "rustfmt", "rust-analyzer", "clippy" ]
|
||||
|
||||
@ -5,10 +5,26 @@ use axum::{
|
||||
http::{HeaderMap, StatusCode},
|
||||
Json,
|
||||
};
|
||||
use visions_types::{Card, CardId, CreateCardRequest, GameId, GameMessage};
|
||||
use visions_types::{Card, CardId, CreateCardRequest, GameId, GameMessage, UpdateCardRequest};
|
||||
|
||||
use crate::{clone, dispatcher::Dispatcher, router::auth_required, state::AppState};
|
||||
|
||||
pub async fn get_card(
|
||||
state: Arc<RwLock<AppState>>,
|
||||
headers: HeaderMap,
|
||||
id: CardId,
|
||||
) -> (StatusCode, Json<Option<Card>>) {
|
||||
auth_required(state.clone(), headers, move |_user| {
|
||||
clone!((state, id), async move {
|
||||
match state.read().await.card(&id) {
|
||||
Some(card) => (StatusCode::OK, card.clone()),
|
||||
None => (StatusCode::NOT_FOUND, Card::default()),
|
||||
}
|
||||
})
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn create_card(
|
||||
state: Arc<RwLock<AppState>>,
|
||||
request: Json<CreateCardRequest>,
|
||||
@ -29,6 +45,26 @@ pub async fn create_card(
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn update_card(
|
||||
state: Arc<RwLock<AppState>>,
|
||||
request: Json<UpdateCardRequest>,
|
||||
headers: HeaderMap,
|
||||
dispatcher: Dispatcher,
|
||||
) -> (StatusCode, Json<Option<String>>) {
|
||||
auth_required(state.clone(), headers, move |user| {
|
||||
clone!((state, request, dispatcher), async move {
|
||||
let Json(UpdateCardRequest(card)) = request;
|
||||
let mut state = state.write().await;
|
||||
let card_id = state.update_card(card);
|
||||
dispatcher
|
||||
.send_to_user(&user.id, GameMessage::UpdateCard(card_id.clone()))
|
||||
.await;
|
||||
(StatusCode::OK, card_id.as_str().to_owned())
|
||||
})
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_all_cards(
|
||||
state: Arc<RwLock<AppState>>,
|
||||
headers: HeaderMap,
|
||||
|
||||
@ -13,7 +13,8 @@ use axum::{
|
||||
};
|
||||
use tower_http::cors::{Any, CorsLayer};
|
||||
use visions_types::{
|
||||
AuthRequest, CharacterId, CreateCardRequest, GameId, GameRequest, SetTabletopImageRequest,
|
||||
AuthRequest, CardId, CharacterId, CreateCardRequest, GameId, GameRequest,
|
||||
SetTabletopImageRequest, UpdateCardRequest,
|
||||
};
|
||||
|
||||
use crate::{check_password, clone, dispatcher::Dispatcher, handlers::*, state::AppState};
|
||||
@ -137,7 +138,24 @@ pub fn api_routes(
|
||||
create_card(state, request, headers, dispatcher)
|
||||
})
|
||||
})
|
||||
.layer(CorsLayer::new().allow_methods([Method::POST])),
|
||||
.put({
|
||||
clone!((state, dispatcher), move |headers: HeaderMap,
|
||||
request: Json<
|
||||
UpdateCardRequest,
|
||||
>| {
|
||||
update_card(state, request, headers, dispatcher)
|
||||
})
|
||||
})
|
||||
.layer(CorsLayer::new().allow_methods([Method::POST, Method::PUT])),
|
||||
)
|
||||
.route(
|
||||
"/cards/{id}",
|
||||
get({
|
||||
clone!(state, move |headers: HeaderMap, Path(id): Path<CardId>| {
|
||||
get_card(state, headers, id)
|
||||
})
|
||||
})
|
||||
.layer(CorsLayer::new().allow_methods([Method::GET])),
|
||||
)
|
||||
.route(
|
||||
"/ws",
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use std::{collections::HashMap, iter::Filter};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use thiserror::Error;
|
||||
use visions_types::*;
|
||||
@ -532,6 +532,10 @@ impl AppState {
|
||||
.filter(|sheet| sheet.game_id == *game_id)
|
||||
}
|
||||
|
||||
pub fn card(&self, card_id: &CardId) -> Option<Card> {
|
||||
self.cards.get(card_id).cloned()
|
||||
}
|
||||
|
||||
pub fn cards<'a>(&'a self, game_id: &'a GameId) -> impl Iterator<Item = &'a Card> {
|
||||
self.cards.values().filter(|card| card.game_id == *game_id)
|
||||
}
|
||||
@ -571,4 +575,10 @@ impl AppState {
|
||||
self.cards.insert(card_id.clone(), card);
|
||||
card_id
|
||||
}
|
||||
|
||||
pub fn update_card(&mut self, card: Card) -> CardId {
|
||||
let card_id = card.id.clone();
|
||||
self.cards.insert(card_id.clone(), card);
|
||||
card_id
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ pub enum GameMessage {
|
||||
PlaceCard(Card, ScreenPosition),
|
||||
Tabletop(String),
|
||||
Title(String),
|
||||
UpdateCard(CardId),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
@ -296,3 +297,6 @@ pub struct ScreenPosition {
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub struct CreateCardRequest(pub Card);
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub struct UpdateCardRequest(pub Card);
|
||||
|
||||
@ -50,6 +50,12 @@ pub trait Client: Clone + PartialEq {
|
||||
url: String,
|
||||
) -> impl Future<Output = Result<(), ClientError>>;
|
||||
|
||||
fn get_card(
|
||||
&self,
|
||||
session_id: &SessionId,
|
||||
card_id: &CardId,
|
||||
) -> impl Future<Output = Result<Card, ClientError>>;
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn get_all_cards(
|
||||
&self,
|
||||
@ -187,6 +193,20 @@ impl Client for Connection {
|
||||
handle_response(response).await
|
||||
}
|
||||
|
||||
async fn get_card(
|
||||
&self,
|
||||
session_id: &SessionId,
|
||||
card_id: &CardId,
|
||||
) -> Result<Card, ClientError> {
|
||||
let response = Request::get(&format!("/api/cards/{}", card_id.as_str()))
|
||||
.header("AUTHORIZATION", &format!("Bearer {}", session_id.as_str()))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
handle_response(response).await
|
||||
}
|
||||
|
||||
async fn get_all_cards(
|
||||
&self,
|
||||
session_id: SessionId,
|
||||
@ -229,6 +249,15 @@ impl Client for Connection {
|
||||
}
|
||||
|
||||
async fn update_card(&self, session_id: SessionId, card: Card) -> Result<(), ClientError> {
|
||||
unimplemented!();
|
||||
let response = Request::put("/api/cards")
|
||||
.header("Content-Type", "application/json")
|
||||
.header("AUTHORIZATION", &format!("Bearer {}", session_id.as_str()))
|
||||
.json(&UpdateCardRequest(card))
|
||||
.unwrap()
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
handle_response(response).await
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,10 +64,13 @@ pub fn CardElement(
|
||||
}
|
||||
);
|
||||
|
||||
let editing: UseStateHandle<bool> = use_state(|| {
|
||||
log!("instantiating editing state");
|
||||
*new_card
|
||||
});
|
||||
log!(
|
||||
"CardElement: {:?} {}",
|
||||
card.title.clone(),
|
||||
card.content.clone()
|
||||
);
|
||||
|
||||
let editing: UseStateHandle<bool> = use_state(|| *new_card);
|
||||
|
||||
use_effect_with(
|
||||
(new_card.clone(), card.clone()),
|
||||
@ -189,7 +192,7 @@ fn EditingCard(
|
||||
let content_ref = NodeRef::default();
|
||||
|
||||
let on_save = Callback::from(clone!(
|
||||
(on_save, title_ref, content_ref, new_card, card),
|
||||
(on_save, on_cancel, title_ref, content_ref, new_card, card),
|
||||
move |_| {
|
||||
let mut card = card.clone();
|
||||
match (
|
||||
@ -200,6 +203,7 @@ fn EditingCard(
|
||||
card.title = Some(title.value());
|
||||
card.content = content.value();
|
||||
on_save.emit((new_card, card));
|
||||
on_cancel.emit(());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@ -155,6 +155,11 @@ fn View(
|
||||
}
|
||||
);
|
||||
|
||||
log!("rendering view");
|
||||
for card in gm_cards {
|
||||
log!("card: {:?} {}", card.title.clone(), card.content.clone());
|
||||
}
|
||||
|
||||
let scene = current_scene
|
||||
.as_ref()
|
||||
.and_then(|id| available_scenes.iter().find(|(sid, _)| *sid == *id))
|
||||
@ -187,7 +192,22 @@ fn View(
|
||||
},
|
||||
};
|
||||
|
||||
let selected_card = use_state(|| (false, None));
|
||||
let selected_card: UseStateHandle<(bool, Option<Card>)> = use_state(|| (false, None));
|
||||
|
||||
use_effect_with(
|
||||
gm_cards.clone(),
|
||||
clone!((gm_cards, selected_card), move |_| {
|
||||
let (_, current_card) = (*selected_card).clone();
|
||||
match current_card {
|
||||
Some(current_card) => {
|
||||
let updated_card = gm_cards.iter().find(|c| c.id == current_card.id);
|
||||
selected_card.set((false, updated_card.cloned()));
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|| ()
|
||||
}),
|
||||
);
|
||||
|
||||
let on_view_card = Callback::from({
|
||||
clone!((gm_cards, selected_card), move |card_id: String| {
|
||||
@ -218,23 +238,6 @@ fn View(
|
||||
})
|
||||
});
|
||||
|
||||
/*
|
||||
let on_edit = Callback::from({
|
||||
clone!((game_id, selected_card), move |_| {
|
||||
selected_card.set((
|
||||
true,
|
||||
Some(Card {
|
||||
id: CardId::default(),
|
||||
game_id: game_id.clone(),
|
||||
location: Location::GM,
|
||||
title: None,
|
||||
content: String::from(""),
|
||||
}),
|
||||
))
|
||||
})
|
||||
});
|
||||
*/
|
||||
|
||||
let cards_page = TabPage {
|
||||
label: AttrValue::from("Cards"),
|
||||
content: html! {
|
||||
@ -442,22 +445,24 @@ pub fn GmView<C: Client + Clone + 'static>(
|
||||
let tabletop_image: UseStateHandle<Option<String>> = use_state(|| None);
|
||||
let gm_cards: UseStateHandle<Vec<Card>> = use_state(|| vec![]);
|
||||
|
||||
/*
|
||||
use_effect_with((session_id.clone(), game.id.clone()), {
|
||||
let client = client.clone();
|
||||
let gm_cards = gm_cards.clone();
|
||||
move |(session_id, game_id)| {
|
||||
let session_id = session_id.clone();
|
||||
let game_id = game_id.clone();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
if let Ok(cards) = client.get_user_cards(session_id, game_id).await {
|
||||
let update_gm_cards = clone!((client, session_id, gm_cards), move |card_id| {
|
||||
wasm_bindgen_futures::spawn_local(clone!((client, session_id, gm_cards), async move {
|
||||
match client.get_card(&session_id, &card_id).await {
|
||||
Ok(updated_card) => {
|
||||
let mut cards: Vec<Card> = gm_cards
|
||||
.iter()
|
||||
.filter(|card| card.id != updated_card.id)
|
||||
.cloned()
|
||||
.collect();
|
||||
cards.push(updated_card);
|
||||
gm_cards.set(cards);
|
||||
}
|
||||
});
|
||||
|| ()
|
||||
}
|
||||
Err(_err) => {
|
||||
log!("failed to get a card");
|
||||
}
|
||||
}
|
||||
}))
|
||||
});
|
||||
*/
|
||||
|
||||
use_effect_with((session_id.clone(), game.id.clone(), client.clone()), {
|
||||
let gm_cards = gm_cards.clone();
|
||||
@ -490,7 +495,8 @@ pub fn GmView<C: Client + Clone + 'static>(
|
||||
available_scenes,
|
||||
available_images,
|
||||
pcs,
|
||||
tabletop_image
|
||||
tabletop_image,
|
||||
update_gm_cards
|
||||
),
|
||||
{
|
||||
move |message: GameMessage, _| match message {
|
||||
@ -506,7 +512,8 @@ pub fn GmView<C: Client + Clone + 'static>(
|
||||
tabletop_image.set(Some(image_url));
|
||||
}
|
||||
GameMessage::Title(title) => scene_title.set(Some(title)),
|
||||
GameMessage::NewCard(card_id) => log!("new card!: {}", card_id.as_str()),
|
||||
GameMessage::NewCard(card_id) => update_gm_cards(card_id),
|
||||
GameMessage::UpdateCard(card_id) => update_gm_cards(card_id),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user