Of which there are an incredible number...
This commit was merged in pull request #394.
This commit is contained in:
@@ -411,7 +411,7 @@ impl App {
|
||||
request.credential,
|
||||
)? {
|
||||
Ok(user) => user,
|
||||
Err(err) => return Ok(Err(err.into())),
|
||||
Err(err) => return Ok(Err(err)),
|
||||
};
|
||||
|
||||
match db.new_session(&user.id) {
|
||||
|
||||
@@ -63,7 +63,7 @@ pub struct SetTabletopImageRequest {
|
||||
|
||||
macro_rules! identifier {
|
||||
($name:ident) => {
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize)]
|
||||
pub struct $name(String);
|
||||
|
||||
impl $name {
|
||||
@@ -103,11 +103,13 @@ macro_rules! identifier {
|
||||
}
|
||||
|
||||
identifier!(AuthenticationId);
|
||||
identifier!(CardId);
|
||||
identifier!(CharacterId);
|
||||
identifier!(GameId);
|
||||
identifier!(InvitationId);
|
||||
identifier!(SceneId);
|
||||
identifier!(SessionId);
|
||||
identifier!(UserId);
|
||||
identifier!(GameId);
|
||||
identifier!(SceneId);
|
||||
identifier!(InvitationId);
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct Invitation {
|
||||
@@ -151,33 +153,6 @@ pub enum AuthResponse {
|
||||
PasswordReset((SessionId, UserOverview)),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub struct CharacterId(String);
|
||||
|
||||
impl CharacterId {
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CharacterId {
|
||||
fn default() -> Self {
|
||||
Self(format!("{}", Uuid::new_v4().hyphenated()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for CharacterId {
|
||||
fn from(s: &str) -> Self {
|
||||
Self(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for CharacterId {
|
||||
fn from(s: String) -> Self {
|
||||
Self(s)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||
pub struct Charsheet {
|
||||
pub id: CharacterId,
|
||||
@@ -187,33 +162,6 @@ pub struct Charsheet {
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub struct CardId(String);
|
||||
|
||||
impl CardId {
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CardId {
|
||||
fn default() -> Self {
|
||||
Self(format!("{}", Uuid::new_v4().hyphenated()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for CardId {
|
||||
fn from(s: &str) -> Self {
|
||||
Self(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for CardId {
|
||||
fn from(s: String) -> Self {
|
||||
Self(s)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||
pub enum Location {
|
||||
#[default]
|
||||
|
||||
@@ -146,7 +146,7 @@ impl Connection {
|
||||
}
|
||||
|
||||
pub async fn config() -> Result<PwaConfig, ClientError> {
|
||||
let response = Request::get(&format!("/api/config"))
|
||||
let response = Request::get("/api/config")
|
||||
.header("Content-Type", "application/json")
|
||||
.send()
|
||||
.await
|
||||
@@ -159,7 +159,7 @@ impl Connection {
|
||||
username: String,
|
||||
password: String,
|
||||
) -> Result<(Connection, UserOverview), ClientError> {
|
||||
let request = Request::post(&format!("/api/auth"))
|
||||
let request = Request::post("/api/auth")
|
||||
.header("Content-Type", "application/json")
|
||||
.body(
|
||||
serde_wasm_bindgen::to_value(
|
||||
@@ -189,7 +189,7 @@ impl Connection {
|
||||
display_name: String,
|
||||
password: String,
|
||||
) -> Result<(), ClientError> {
|
||||
let request = Request::post(&format!("/api/users"))
|
||||
let request = Request::post("/api/users")
|
||||
.header("Content-Type", "application/json")
|
||||
.body(
|
||||
serde_wasm_bindgen::to_value(
|
||||
@@ -218,7 +218,7 @@ impl Connection {
|
||||
user_name: String,
|
||||
display_name: String,
|
||||
) -> Result<StartRegisterPasskeyResponse, ClientError> {
|
||||
let request = Request::post(&format!("/api/passkey-register/start"))
|
||||
let request = Request::post("/api/passkey-register/start")
|
||||
.header("Content-Type", "application/json")
|
||||
.body(
|
||||
serde_wasm_bindgen::to_value(
|
||||
@@ -245,7 +245,7 @@ impl Connection {
|
||||
user_id: UserId,
|
||||
credential: RegisterPublicKeyCredential,
|
||||
) -> Result<(), ClientError> {
|
||||
let request = Request::post(&format!("/api/passkey-register/finish"))
|
||||
let request = Request::post("/api/passkey-register/finish")
|
||||
.header("Content-Type", "application/json")
|
||||
.body(
|
||||
serde_wasm_bindgen::to_value(
|
||||
@@ -268,7 +268,7 @@ impl Connection {
|
||||
}
|
||||
|
||||
pub async fn start_auth_with_passkey() -> Result<StartPasskeyAuthResponse, ClientError> {
|
||||
let request = Request::post(&format!("/api/passkey-auth/start"))
|
||||
let request = Request::post("/api/passkey-auth/start")
|
||||
.header("Content-Type", "application/json");
|
||||
|
||||
handle_response(
|
||||
@@ -284,7 +284,7 @@ impl Connection {
|
||||
auth_id: AuthenticationId,
|
||||
credential: PublicKeyCredential,
|
||||
) -> Result<(Connection, UserOverview), ClientError> {
|
||||
let request = Request::post(&format!("/api/passkey-auth/finish"))
|
||||
let request = Request::post("/api/passkey-auth/finish")
|
||||
.header("Content-Type", "application/json")
|
||||
.body(
|
||||
serde_wasm_bindgen::to_value(
|
||||
@@ -337,7 +337,7 @@ impl Client for Connection {
|
||||
}
|
||||
|
||||
async fn list_invitations(&self) -> Result<Vec<Invitation>, ClientError> {
|
||||
let response: Response = Request::get(&format!("/api/invitations"))
|
||||
let response: Response = Request::get("/api/invitations")
|
||||
.header("Content-Type", "application/json")
|
||||
.header(
|
||||
"Authorization",
|
||||
@@ -351,7 +351,7 @@ impl Client for Connection {
|
||||
}
|
||||
|
||||
async fn create_invitation(&self, display_name: String) -> Result<Invitation, ClientError> {
|
||||
let request = Request::put(&format!("/api/invitations"))
|
||||
let request = Request::put("/api/invitations")
|
||||
.header("Content-Type", "application/json")
|
||||
.header(
|
||||
"Authorization",
|
||||
@@ -374,7 +374,7 @@ impl Client for Connection {
|
||||
}
|
||||
|
||||
async fn get_self(&self) -> Result<UserOverview, ClientError> {
|
||||
let response: Response = Request::get(&format!("/api/self"))
|
||||
let response: Response = Request::get("/api/self")
|
||||
.header("Content-Type", "application/json")
|
||||
.header(
|
||||
"Authorization",
|
||||
@@ -388,7 +388,7 @@ impl Client for Connection {
|
||||
}
|
||||
|
||||
async fn list_users(&self) -> Result<Vec<UserOverview>, ClientError> {
|
||||
let response: Response = Request::get(&format!("/api/users"))
|
||||
let response: Response = Request::get("/api/users")
|
||||
.header("Content-Type", "application/json")
|
||||
.header(
|
||||
"Authorization",
|
||||
@@ -570,7 +570,7 @@ impl Client for Connection {
|
||||
if let Some(user_id) = filter.user_id {
|
||||
query_params.append("user_id", user_id.as_str());
|
||||
}
|
||||
let query_str = format!("/api/characters?{}", query_params.to_string());
|
||||
let query_str = format!("/api/characters?{}", query_params);
|
||||
|
||||
let response: Response = Request::get(&query_str)
|
||||
.header("Content-Type", "application/json")
|
||||
@@ -585,7 +585,7 @@ impl Client for Connection {
|
||||
let sheets: Vec<Charsheet> = handle_response(response).await?;
|
||||
Ok(sheets
|
||||
.into_iter()
|
||||
.flat_map(|sheet| Character::try_from(sheet))
|
||||
.flat_map(Character::try_from)
|
||||
.collect())
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ pub fn CardElement(
|
||||
let editing: UseStateHandle<bool> = use_state(|| *new_card);
|
||||
|
||||
use_effect_with(
|
||||
(new_card.clone(), card.clone()),
|
||||
(*new_card, card.clone()),
|
||||
clone!((editing), move |(new_card, _)| {
|
||||
editing.set(*new_card);
|
||||
|| ()
|
||||
@@ -197,17 +197,14 @@ fn EditingCard(
|
||||
(on_save, on_cancel, title_ref, content_ref, new_card, card),
|
||||
move |_| {
|
||||
let mut card = card.clone();
|
||||
match (
|
||||
if let (Some(title), Some(content)) = (
|
||||
title_ref.cast::<HtmlInputElement>(),
|
||||
content_ref.cast::<HtmlInputElement>(),
|
||||
) {
|
||||
(Some(title), Some(content)) => {
|
||||
card.title = Some(title.value());
|
||||
card.content = content.value();
|
||||
on_save.emit((new_card, card));
|
||||
on_cancel.emit(());
|
||||
}
|
||||
_ => {}
|
||||
card.title = Some(title.value());
|
||||
card.content = content.value();
|
||||
on_save.emit((new_card, card));
|
||||
on_cancel.emit(());
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
@@ -66,8 +66,6 @@ fn CharacterRow(
|
||||
width: 10%;
|
||||
);
|
||||
|
||||
let system_sheet = Character::try_from(sheet.clone()).unwrap();
|
||||
|
||||
let on_edit = use_callback((sheet.clone(), on_edit.clone()), |_, (sheet, on_edit)| {
|
||||
on_edit.emit(sheet.id())
|
||||
});
|
||||
@@ -84,7 +82,7 @@ fn CharacterRow(
|
||||
|
||||
html! {
|
||||
<Row class={classes!(style.clone())}>
|
||||
{system_sheet.character_list_row()}
|
||||
{sheet.character_list_row()}
|
||||
<div class={edit_button_size.clone()}>
|
||||
{edit_button}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use yew::prelude::*;
|
||||
|
||||
pub trait DropMenuOption: PartialEq {
|
||||
fn as_html(self: &Self, selected: bool) -> Html;
|
||||
fn as_html(&self, selected: bool) -> Html;
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
use stylist::css;
|
||||
use visions_types::{CharacterId, GameOverview, UserId, UserOverview};
|
||||
use visions_types::{CharacterId, GameOverview, UserOverview};
|
||||
use yew::prelude::*;
|
||||
|
||||
use crate::{
|
||||
clone,
|
||||
components::*,
|
||||
design,
|
||||
systems::{Character, SystemName, UnsupportedGameType, SYSTEMS},
|
||||
types::GameEditTab,
|
||||
systems::{Character, SystemName, SYSTEMS},
|
||||
};
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
@@ -61,6 +60,7 @@ pub fn NewGame(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct EditGameProps_ {
|
||||
tab: GameEditTab,
|
||||
@@ -173,8 +173,7 @@ pub fn EditGame(
|
||||
serde_json::from_str(&overview.system).map_err(|_| UnsupportedGameType);
|
||||
|
||||
let (players, unattached_users): (Vec<UserOverview>, Vec<UserOverview>) = users
|
||||
.iter()
|
||||
.map(|user| user.clone())
|
||||
.iter().cloned()
|
||||
.partition(|user| overview.players.contains(&user.id) || user.id == overview.gm);
|
||||
|
||||
let (gms, players): (Vec<UserOverview>, Vec<UserOverview>) =
|
||||
@@ -259,6 +258,7 @@ pub fn AddRow(AddProps { user, on_add }: &AddProps) -> Html {
|
||||
</Row>
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ViewGameDetailsProps {
|
||||
@@ -356,8 +356,6 @@ fn CharacterRow(
|
||||
width: 10%;
|
||||
);
|
||||
|
||||
let system_sheet = Character::try_from(sheet.clone()).unwrap();
|
||||
|
||||
let on_edit = use_callback((sheet.clone(), on_edit.clone()), |_, (sheet, on_edit)| {
|
||||
on_edit.emit(sheet.id())
|
||||
});
|
||||
@@ -374,7 +372,7 @@ fn CharacterRow(
|
||||
|
||||
html! {
|
||||
<Row class={classes!(design::border_invisible(), row_coloration.clone())}>
|
||||
{system_sheet.character_list_row()}
|
||||
{sheet.character_list_row()}
|
||||
<div class={edit_button_size.clone()}>
|
||||
{edit_button}
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@ use serde::Serialize;
|
||||
use wasm_bindgen::JsValue;
|
||||
use yew::prelude::*;
|
||||
|
||||
use crate::{clone, components::*};
|
||||
use crate::components::*;
|
||||
|
||||
#[derive(Clone, PartialEq, Serialize)]
|
||||
pub enum Role {
|
||||
@@ -40,6 +40,7 @@ impl From<Role> for JsValue {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct EditUserProps {
|
||||
pub username: Option<String>,
|
||||
@@ -69,7 +70,7 @@ pub fn EditUser(
|
||||
move || email
|
||||
});
|
||||
let admin_field = use_state({
|
||||
let admin = admin.clone();
|
||||
let admin = *admin;
|
||||
move || admin
|
||||
});
|
||||
|
||||
@@ -86,10 +87,7 @@ pub fn EditUser(
|
||||
|
||||
let on_save_ = Callback::from(clone!(
|
||||
(on_save, username_field, email_field, admin_field),
|
||||
move |_| match ((*username_field).clone(), (*email_field).clone()) {
|
||||
(Some(unf), Some(ef)) => on_save.emit((unf, ef, *admin_field, Role::Gm)),
|
||||
_ => (),
|
||||
}
|
||||
move |_| if let (Some(unf), Some(ef)) = ((*username_field).clone(), (*email_field).clone()) { on_save.emit((unf, ef, *admin_field, Role::Gm)) }
|
||||
));
|
||||
|
||||
html! {
|
||||
@@ -108,3 +106,4 @@ pub fn EditUser(
|
||||
</div>
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -53,10 +53,10 @@ pub fn CollapsableSidebar(
|
||||
let child_container_style = css!(
|
||||
overflow-y: auto;
|
||||
);
|
||||
let is_visible = use_state(|| visible.clone());
|
||||
let is_visible = use_state(|| *visible);
|
||||
|
||||
let on_toggle_sidebar = use_callback(is_visible.clone(), |_, is_visible| {
|
||||
let visible = (**is_visible).clone();
|
||||
let visible = **is_visible ;
|
||||
is_visible.set(!visible);
|
||||
});
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ pub fn DiscreteMeter(
|
||||
<MeterStep
|
||||
value={i}
|
||||
state={state}
|
||||
variant={variant.clone()} />
|
||||
variant={*variant} />
|
||||
}
|
||||
})
|
||||
.collect::<Html>()
|
||||
@@ -236,7 +236,7 @@ fn MeterStep(
|
||||
);
|
||||
|
||||
let on_mouse_enter = use_callback(
|
||||
(value.clone(), on_hover.clone()),
|
||||
(*value, on_hover.clone()),
|
||||
|_: MouseEvent, (value, on_hover)| {
|
||||
if let Some(ref on_hover) = on_hover {
|
||||
on_hover.emit(*value);
|
||||
@@ -265,12 +265,12 @@ fn MeterStep(
|
||||
MeterVariant::Dots => dot_style,
|
||||
};
|
||||
|
||||
let element = match state {
|
||||
|
||||
|
||||
match state {
|
||||
MeterStepState::Filled => bar_element(classes!(element_style, bar_step_filled)),
|
||||
MeterStepState::Unfilled => bar_element(classes!(element_style)),
|
||||
MeterStepState::Unavailable => bar_element(classes!(element_style, bar_step_unavailable)),
|
||||
MeterStepState::Hover => bar_element(classes!(element_style, bar_step_hover)),
|
||||
};
|
||||
|
||||
element
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,10 +67,10 @@ impl GuageState {
|
||||
}
|
||||
|
||||
fn show_operations(&self) -> bool {
|
||||
match self.mode {
|
||||
GuageMode::OperationsVisible | GuageMode::StartEditing(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(
|
||||
self.mode,
|
||||
GuageMode::OperationsVisible | GuageMode::StartEditing(_)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,8 +101,8 @@ impl Reducible for GuageState {
|
||||
},
|
||||
GuageAction::ActivateEditing(opcode) => {
|
||||
let text = match opcode {
|
||||
OpCode::Add => format!("+ 1"),
|
||||
OpCode::Subtract => format!("- 1"),
|
||||
OpCode::Add => "+ 1".to_string(),
|
||||
OpCode::Subtract => "- 1".to_string(),
|
||||
OpCode::Set => format!("= {}", self.current),
|
||||
};
|
||||
Self {
|
||||
@@ -218,9 +218,8 @@ pub fn Guage(
|
||||
};
|
||||
|
||||
let on_focus = Callback::from(clone!(state, move |_| {
|
||||
match state.mode {
|
||||
GuageMode::Idle => state.dispatch(GuageAction::ShowOperations),
|
||||
_ => {}
|
||||
if let GuageMode::Idle = state.mode {
|
||||
state.dispatch(GuageAction::ShowOperations)
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ pub fn ModalElement(
|
||||
if *open {
|
||||
let _ = modal.show_modal();
|
||||
} else {
|
||||
let _ = modal.close();
|
||||
modal.close();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ pub fn SelectionButton<T: ListItem + Clone + 'static>(
|
||||
let last_idx = options.len();
|
||||
|
||||
let buttons: Vec<Html> = options
|
||||
.into_iter()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, option)| {
|
||||
let style = if idx == 0 {
|
||||
|
||||
@@ -80,9 +80,9 @@ fn ListItemComponent<T: ListItem + Clone + 'static>(
|
||||
}: &ListItemProps<T>,
|
||||
) -> Html {
|
||||
let classes = if *enabled {
|
||||
format!("list-selector__item_enabled")
|
||||
"list-selector__item_enabled".to_string()
|
||||
} else {
|
||||
format!("list-selector__item")
|
||||
"list-selector__item".to_string()
|
||||
};
|
||||
let on_click: Callback<MouseEvent> =
|
||||
Callback::from(clone!((on_select, item), move |_| on_select
|
||||
|
||||
@@ -22,7 +22,6 @@ mod edit_game;
|
||||
pub use edit_game::*;
|
||||
|
||||
mod edit_user;
|
||||
pub use edit_user::*;
|
||||
|
||||
mod foundation;
|
||||
pub use foundation::*;
|
||||
|
||||
@@ -40,8 +40,7 @@ pub fn PlayerList(
|
||||
);
|
||||
|
||||
let (players, unattached_users): (Vec<UserOverview>, Vec<UserOverview>) = users
|
||||
.iter()
|
||||
.map(|user| user.clone())
|
||||
.iter().cloned()
|
||||
.partition(|user| players.contains(&user.id));
|
||||
|
||||
html! {
|
||||
|
||||
@@ -52,11 +52,8 @@ pub fn TabView(
|
||||
let on_select = Callback::from(clone!(
|
||||
(pages, current_page_id),
|
||||
move |page_name: String| {
|
||||
match pages.iter().find(|page| page.id() == page_name) {
|
||||
Some(page) => {
|
||||
current_page_id.set(page.id().to_string());
|
||||
}
|
||||
None => {}
|
||||
if let Some(page) = pages.iter().find(|page| page.id() == page_name) {
|
||||
current_page_id.set(page.id().to_string());
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use stylist::css;
|
||||
use visions_types::Card;
|
||||
use yew::prelude::*;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use stylist::css;
|
||||
use yew::prelude::*;
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(PartialEq, Properties)]
|
||||
pub struct TooltipProps {
|
||||
#[prop_or_default]
|
||||
@@ -11,6 +12,7 @@ pub struct TooltipProps {
|
||||
pub children: Html,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[function_component]
|
||||
pub fn Tooltip(
|
||||
TooltipProps {
|
||||
|
||||
@@ -56,7 +56,7 @@ pub fn WebsocketProvider<C: Client + Clone + 'static>(
|
||||
(client.clone(), game_id.clone(), on_message.clone()),
|
||||
clone!(socket_state, move |(client, game_id, on_message)| {
|
||||
let ws_client = client.connect_to_game(
|
||||
&game_id,
|
||||
game_id,
|
||||
on_message.clone(),
|
||||
Box::new(clone!(socket_state, move |_evt| {
|
||||
log!("on close");
|
||||
|
||||
@@ -131,7 +131,7 @@ pub fn border_default(hoverable: bool) -> Classes {
|
||||
let hover_style = if hoverable {
|
||||
css!(
|
||||
:hover {
|
||||
box-shadow: ${*&hover_shadow};
|
||||
box-shadow: ${hover_shadow};
|
||||
}
|
||||
)
|
||||
} else {
|
||||
|
||||
@@ -61,13 +61,11 @@ impl AppState {
|
||||
&self.base_url
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn authenticated(&self) -> bool {
|
||||
if let ConnectionState::Authenticated { .. } = self.connection {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self.connection, ConnectionState::Authenticated { .. })
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn client(&self) -> Option<&Connection> {
|
||||
if let ConnectionState::Authenticated { ref client, .. } = self.connection {
|
||||
@@ -174,15 +172,14 @@ pub fn StateProvider(StateProviderProps { base_url, children }: &StateProviderPr
|
||||
}
|
||||
|
||||
async fn hydrate_client(app_state: UseReducerHandle<AppState>) {
|
||||
match web_sys::window() {
|
||||
Some(window) => match window.session_storage() {
|
||||
if let Some(window) = web_sys::window() {
|
||||
match window.session_storage() {
|
||||
Ok(Some(storage)) => hydrate_client_(&window, &storage, &app_state).await,
|
||||
_ => {
|
||||
// Need to convert this into an error that can then be displayed
|
||||
error!("no session ID detected");
|
||||
}
|
||||
},
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +192,7 @@ async fn hydrate_client_(
|
||||
let session_id: SessionId = session_id.into();
|
||||
let client = Connection::new(session_id.clone());
|
||||
match client.get_self().await {
|
||||
Ok(user) => set_session(&app_state, client, user),
|
||||
Ok(user) => set_session(app_state, client, user),
|
||||
Err(ClientError::Unauthorized) => {
|
||||
clear_session(app_state);
|
||||
window
|
||||
|
||||
@@ -29,12 +29,14 @@ pub struct Candela {
|
||||
scars: Vec<String>,
|
||||
}
|
||||
|
||||
/*
|
||||
#[derive(Clone, PartialEq, Eq, Deserialize, Debug, Default)]
|
||||
struct ActionGroup {
|
||||
max_drives: u8,
|
||||
drives: u8,
|
||||
resistances: u8,
|
||||
}
|
||||
*/
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Deserialize, Serialize, Debug, Default)]
|
||||
struct Action {
|
||||
@@ -157,14 +159,13 @@ pub fn ActionGroupHeader(
|
||||
sheet_editing,
|
||||
}: &ActionGroupHeaderProperties,
|
||||
) -> Html {
|
||||
let on_update: Callback<u8> =
|
||||
use_callback((state.clone(), name.clone()), |value, (state, name)| {
|
||||
state.dispatch(SheetAction::UpdateDrive(name.clone(), value))
|
||||
});
|
||||
let on_update: Callback<u8> = use_callback((state.clone(), *name), |value, (state, name)| {
|
||||
state.dispatch(SheetAction::UpdateDrive(*name, value))
|
||||
});
|
||||
|
||||
let on_change_max: Callback<u8> =
|
||||
use_callback((state.clone(), name.clone()), |value, (state, name)| {
|
||||
state.dispatch(SheetAction::UpdateDriveMax(name.clone(), value))
|
||||
use_callback((state.clone(), *name), |value, (state, name)| {
|
||||
state.dispatch(SheetAction::UpdateDriveMax(*name, value))
|
||||
});
|
||||
|
||||
html! {
|
||||
@@ -207,14 +208,12 @@ pub fn ActionElement(
|
||||
let gilded_symbol = if *gilded { "◆" } else { "◇" };
|
||||
|
||||
let editable = on_update.is_some();
|
||||
let on_update: Callback<u8> = use_callback(
|
||||
(name.clone(), on_update.clone()),
|
||||
|value, (name, on_update)| {
|
||||
let on_update: Callback<u8> =
|
||||
use_callback((*name, on_update.clone()), |value, (name, on_update)| {
|
||||
if let Some(on_update) = on_update {
|
||||
on_update.emit((name.clone(), value));
|
||||
on_update.emit((*name, value));
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
html! {
|
||||
<div class={styles}>
|
||||
|
||||
@@ -69,7 +69,7 @@ impl From<&SystemName> for JsValue {
|
||||
|
||||
impl From<SystemName> for JsValue {
|
||||
fn from(r: SystemName) -> JsValue {
|
||||
r.into()
|
||||
JsValue::from(&r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,9 +192,10 @@ impl Character {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Character {
|
||||
/*
|
||||
* impl PartialOrd for Character {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.name().cmp(&other.name()))
|
||||
Some(self.name().cmp(other.name()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,6 +204,7 @@ impl Ord for Character {
|
||||
self.name().cmp(other.name())
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
pub enum CharacterDetails {
|
||||
@@ -242,19 +244,19 @@ impl TryFrom<Charsheet> for Character {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Charsheet> for Character {
|
||||
fn into(self) -> Charsheet {
|
||||
let system = match self.details {
|
||||
impl From<Character> for Charsheet {
|
||||
fn from(val: Character) -> Self {
|
||||
let system = match val.details {
|
||||
CharacterDetails::Candela(_) => SystemName::Candela,
|
||||
CharacterDetails::Cypher(_) => SystemName::Cypher,
|
||||
};
|
||||
|
||||
let data = serde_json::to_vec(&self.details).unwrap();
|
||||
let data = serde_json::to_vec(&val.details).unwrap();
|
||||
|
||||
Charsheet {
|
||||
id: self.id,
|
||||
game_id: self.game_id,
|
||||
user_id: self.user_id,
|
||||
id: val.id,
|
||||
game_id: val.game_id,
|
||||
user_id: val.user_id,
|
||||
system: serde_json::to_string(&system).unwrap(),
|
||||
data,
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ pub fn navigate_to(path: &str) {
|
||||
Some(window) => {
|
||||
window
|
||||
.location()
|
||||
.set_pathname(&path)
|
||||
.set_pathname(path)
|
||||
.expect("path navigation to succeed");
|
||||
}
|
||||
None => {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use gloo_console::log;
|
||||
use jiff::Timestamp;
|
||||
use stylist::css;
|
||||
use visions_types::*;
|
||||
@@ -16,6 +15,7 @@ use crate::{
|
||||
};
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[derive(Default)]
|
||||
struct ViewState {
|
||||
users: Vec<UserOverview>,
|
||||
invitations: Vec<Invitation>,
|
||||
@@ -23,16 +23,6 @@ struct ViewState {
|
||||
invitation_modal_is_open: bool,
|
||||
}
|
||||
|
||||
impl Default for ViewState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
users: vec![],
|
||||
invitations: vec![],
|
||||
error: None,
|
||||
invitation_modal_is_open: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ViewStateAction {
|
||||
SetUsers(Vec<UserOverview>),
|
||||
@@ -54,7 +44,7 @@ impl Reducible for ViewState {
|
||||
..(*self).clone()
|
||||
},
|
||||
ViewStateAction::SetInvitations(invitations) => Self {
|
||||
invitations: invitations,
|
||||
invitations,
|
||||
error: None,
|
||||
..(*self).clone()
|
||||
},
|
||||
@@ -90,7 +80,7 @@ impl Reducible for ViewState {
|
||||
#[function_component]
|
||||
pub fn Admin() -> Html {
|
||||
let app_state = use_context::<UseReducerHandle<AppState>>().expect("app state not found");
|
||||
let view_state: UseReducerHandle<ViewState> = use_reducer(|| ViewState::default());
|
||||
let view_state: UseReducerHandle<ViewState> = use_reducer(ViewState::default);
|
||||
|
||||
let client = app_state.client();
|
||||
|
||||
@@ -148,7 +138,7 @@ struct UserListProps {
|
||||
|
||||
#[function_component]
|
||||
fn UserList(UserListProps { users }: &UserListProps) -> Html {
|
||||
let _user_list: UseStateHandle<Vec<UserOverview>> = use_state(|| vec![]);
|
||||
let _user_list: UseStateHandle<Vec<UserOverview>> = use_state(std::vec::Vec::new);
|
||||
|
||||
html! {
|
||||
<div>
|
||||
@@ -179,8 +169,7 @@ pub fn InvitationList(
|
||||
) -> Html {
|
||||
let invitations: Vec<Html> = invitations
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, invitation)| {
|
||||
.map(|invitation| {
|
||||
html! {
|
||||
<InvitationRow
|
||||
invitation={invitation.clone()}
|
||||
|
||||
@@ -43,31 +43,25 @@ fn populate_data<C: Client + Clone + 'static>(
|
||||
id: CharacterId,
|
||||
) {
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
match client {
|
||||
Some(client) => match client.character(&id).await {
|
||||
Ok(Ok(character)) => character_state.set(Some(character)),
|
||||
Ok(Err(err)) => {
|
||||
error!(format!("{:?}", err));
|
||||
todo!()
|
||||
}
|
||||
Err(_) => todo!(),
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
if let Some(client) = client { match client.character(&id).await {
|
||||
Ok(Ok(character)) => character_state.set(Some(character)),
|
||||
Ok(Err(err)) => {
|
||||
error!(format!("{:?}", err));
|
||||
todo!()
|
||||
}
|
||||
Err(_) => todo!(),
|
||||
} }
|
||||
})
|
||||
}
|
||||
|
||||
fn save_character<C: Client + Clone + 'static>(client: Option<C>, character: Character) {
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
match client {
|
||||
Some(client) => match client.update_character(character).await {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
error!(format!("{:?}", err));
|
||||
todo!();
|
||||
}
|
||||
},
|
||||
None => {}
|
||||
};
|
||||
if let Some(client) = client { match client.update_character(character).await {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
error!(format!("{:?}", err));
|
||||
todo!();
|
||||
}
|
||||
} };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use visions_types::*;
|
||||
use yew::prelude::*;
|
||||
use yew_router::prelude::*;
|
||||
|
||||
use crate::{clone, components::*, design, types::GameEditTab};
|
||||
use crate::{clone, components::*, design};
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct DesignProps {}
|
||||
@@ -20,9 +20,8 @@ pub enum Page {
|
||||
#[at("/design/card")]
|
||||
Card,
|
||||
|
||||
#[at("/design/edit-game")]
|
||||
EditGame,
|
||||
|
||||
// #[at("/design/edit-game")]
|
||||
// EditGame,
|
||||
#[at("/design/fields-labels")]
|
||||
FieldsLabels,
|
||||
|
||||
@@ -54,7 +53,7 @@ impl ListItem for Page {
|
||||
Page::Buttons => "buttons",
|
||||
Page::Card => "card",
|
||||
Page::Default => "",
|
||||
Page::EditGame => "edit-game",
|
||||
// Page::EditGame => "edit-game",
|
||||
Page::FieldsLabels => "fields-labels",
|
||||
Page::GameManagement => "game-management",
|
||||
Page::Modal => "modal",
|
||||
@@ -82,72 +81,61 @@ impl ListItem for Page {
|
||||
match self {
|
||||
Page::Buttons => {
|
||||
html! { <div onclick={Callback::from({
|
||||
let on_navigate = on_navigate.clone();
|
||||
move |_| on_navigate(Page::Buttons)
|
||||
})}>{"Buttons"}</div> }
|
||||
}
|
||||
Page::Card => {
|
||||
html! { <div onclick={Callback::from({
|
||||
let on_navigate = on_navigate.clone();
|
||||
move |_| on_navigate(Page::Card)
|
||||
})}>{"Card"}</div> }
|
||||
}
|
||||
Page::Default => {
|
||||
html! { <div onclick={Callback::from({
|
||||
let on_navigate = on_navigate.clone();
|
||||
move |_| on_navigate(Page::Default)
|
||||
})}>{"Default"}</div> }
|
||||
}
|
||||
Page::EditGame => {
|
||||
html! { <div onclick={Callback::from({
|
||||
let on_navigate = on_navigate.clone();
|
||||
move |_| on_navigate(Page::EditGame)
|
||||
})}>{"Edit Game"}</div> }
|
||||
}
|
||||
// Page::EditGame => {
|
||||
// html! { <div onclick={Callback::from({
|
||||
// move |_| on_navigate(Page::EditGame)
|
||||
// })}>{"Edit Game"}</div> }
|
||||
// }
|
||||
Page::FieldsLabels => {
|
||||
html! { <div onclick={Callback::from({
|
||||
let on_navigate = on_navigate.clone();
|
||||
move |_| on_navigate(Page::FieldsLabels)
|
||||
})}>{"Fields and Labels"}</div> }
|
||||
}
|
||||
Page::GameManagement => {
|
||||
html! { <div onclick={Callback::from({
|
||||
let on_navigate = on_navigate.clone();
|
||||
move |_| on_navigate(Page::GameManagement)
|
||||
})}>{"Game Management"}</div> }
|
||||
}
|
||||
Page::Modal => {
|
||||
html! { <div onclick={Callback::from({
|
||||
let on_navigate = on_navigate.clone();
|
||||
move |_| on_navigate(Page::Modal)
|
||||
})}>{"Modal Dialogs"}</div> }
|
||||
}
|
||||
Page::Panel => {
|
||||
html! { <div onclick={Callback::from({
|
||||
let on_navigate = on_navigate.clone();
|
||||
move |_| on_navigate(Page::Panel)
|
||||
})}>{"Panel"}</div> }
|
||||
}
|
||||
Page::SemanticTokens => {
|
||||
html! { <div onclick={Callback::from(
|
||||
clone!(on_navigate, move |_| on_navigate(Page::SemanticTokens))
|
||||
move |_| on_navigate(Page::SemanticTokens)
|
||||
)}>{"Semantic Tokens"}</div> }
|
||||
}
|
||||
Page::Swatches => {
|
||||
html! { <div onclick={Callback::from({
|
||||
let on_navigate = on_navigate.clone();
|
||||
move |_| on_navigate(Page::Swatches)
|
||||
})}>{"Swatches"}</div> }
|
||||
}
|
||||
Page::Tabs => {
|
||||
html! { <div onclick={Callback::from({
|
||||
let on_navigate = on_navigate.clone();
|
||||
move |_| on_navigate(Page::Tabs)
|
||||
})}>{"Tabs"}</div> }
|
||||
}
|
||||
Page::UserManagement => {
|
||||
html! { <div onclick={Callback::from({
|
||||
let on_navigate = on_navigate.clone();
|
||||
move |_| on_navigate(Page::UserManagement)
|
||||
})}>{"User Management"}</div> }
|
||||
}
|
||||
@@ -231,7 +219,7 @@ fn switch(route: Page) -> Html {
|
||||
Page::Buttons => html! { <ButtonsPage /> },
|
||||
Page::Card => html! { <CardsPage /> },
|
||||
Page::Default => html! { <SwatchPage /> },
|
||||
Page::EditGame => html! { <EditGamePage /> },
|
||||
// Page::EditGame => html! { <EditGamePage /> },
|
||||
Page::FieldsLabels => html! { <FieldsLabelsPage /> },
|
||||
Page::Modal => html! { <ModalPage /> },
|
||||
Page::Panel => html! { <PanelPage /> },
|
||||
@@ -265,7 +253,7 @@ fn switch(route: Page) -> Html {
|
||||
Page::Modal,
|
||||
Page::UserManagement,
|
||||
Page::GameManagement,
|
||||
Page::EditGame
|
||||
// Page::EditGame
|
||||
]} />
|
||||
<div class="design__main">
|
||||
{page}
|
||||
@@ -287,6 +275,7 @@ pub fn Design(DesignProps {}: &DesignProps) -> Html {
|
||||
enum SelectionOptions {
|
||||
Passkey,
|
||||
Password,
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
SSO,
|
||||
}
|
||||
|
||||
@@ -599,6 +588,7 @@ pub fn TabsPage() -> Html {
|
||||
html! { <TabView pages={vec![page1, page2]}/> }
|
||||
}
|
||||
|
||||
/*
|
||||
#[function_component]
|
||||
pub fn EditGamePage() -> Html {
|
||||
let savanni = UserOverview {
|
||||
@@ -659,3 +649,4 @@ pub fn EditGamePage() -> Html {
|
||||
</>
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use gloo_console::log;
|
||||
use stylist::css;
|
||||
use visions_types::*;
|
||||
use yew::prelude::*;
|
||||
|
||||
@@ -7,7 +6,6 @@ use crate::{
|
||||
client::{CharacterFilter, Client},
|
||||
clone,
|
||||
components::*,
|
||||
design,
|
||||
state::AppState,
|
||||
systems::Character,
|
||||
types::{GameEditTab, GAME_EDIT_TABS},
|
||||
@@ -23,6 +21,7 @@ struct ViewState {
|
||||
characters: Vec<Character>,
|
||||
}
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
enum ViewStateAction {
|
||||
SetPage(GameEditTab),
|
||||
SetGame(GameOverview),
|
||||
@@ -114,7 +113,7 @@ pub struct GameDetailsViewProps {
|
||||
#[function_component]
|
||||
pub fn GameDetailsView(GameDetailsViewProps { id }: &GameDetailsViewProps) -> Html {
|
||||
let app_state = use_context::<UseReducerHandle<AppState>>().expect("app state not found");
|
||||
let state = use_reducer(|| ViewState::default());
|
||||
let state = use_reducer(ViewState::default);
|
||||
|
||||
let actions: GameEditActions = game_details_actions(&app_state, &state);
|
||||
|
||||
@@ -127,19 +126,19 @@ pub fn GameDetailsView(GameDetailsViewProps { id }: &GameDetailsViewProps) -> Ht
|
||||
|
||||
match app_state.user() {
|
||||
Some(user) => {
|
||||
let overview = (*state).overview.as_ref();
|
||||
let users: &Vec<UserOverview> = (*state).users.as_ref();
|
||||
let characters: &Vec<Character> = (*state).characters.as_ref();
|
||||
let overview = state.overview.as_ref();
|
||||
let users: &Vec<UserOverview> = state.users.as_ref();
|
||||
let characters: &Vec<Character> = state.characters.as_ref();
|
||||
let editable = match overview {
|
||||
Some(overview) => user.admin || overview.gm == user.id,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let gm: Option<UserOverview> = (*state)
|
||||
let gm: Option<UserOverview> = state
|
||||
.overview
|
||||
.as_ref()
|
||||
.map(|game| game.gm.clone())
|
||||
.and_then(|gm_id| (*state).users.iter().find(|user| user.id == gm_id))
|
||||
.and_then(|gm_id| state.users.iter().find(|user| user.id == gm_id))
|
||||
.cloned();
|
||||
|
||||
let current_view = match (state.game_edit_tab.clone(), gm.clone()) {
|
||||
@@ -150,9 +149,9 @@ pub fn GameDetailsView(GameDetailsViewProps { id }: &GameDetailsViewProps) -> Ht
|
||||
/>
|
||||
|
||||
},
|
||||
(GameEditTab::Players, Some(gm)) => html! {
|
||||
(GameEditTab::Players, Some(_gm)) => html! {
|
||||
<PlayerList
|
||||
users={(*state).users.clone()}
|
||||
users={state.users.clone()}
|
||||
players={state.overview.clone().map(|overview| overview.players.iter().cloned().collect()).unwrap_or(vec![])}
|
||||
on_add_player={actions.on_add_player}
|
||||
on_remove_player={actions.on_remove_player} />
|
||||
@@ -171,7 +170,7 @@ pub fn GameDetailsView(GameDetailsViewProps { id }: &GameDetailsViewProps) -> Ht
|
||||
html! {
|
||||
<div>
|
||||
<Row>
|
||||
<TextEntry placeholder="game name" value={(*overview).name.clone()} />
|
||||
<TextEntry placeholder="game name" value={overview.name.clone()} />
|
||||
<div>{format!("{}", overview.system.to_string())}</div>
|
||||
</Row>
|
||||
|
||||
@@ -181,7 +180,7 @@ pub fn GameDetailsView(GameDetailsViewProps { id }: &GameDetailsViewProps) -> Ht
|
||||
</Row>
|
||||
|
||||
<SelectionButton<GameEditTab>
|
||||
selected={(*state).game_edit_tab.clone()}
|
||||
selected={state.game_edit_tab.clone()}
|
||||
options={Vec::from(GAME_EDIT_TABS.clone())}
|
||||
on_change={change_page}
|
||||
/>
|
||||
@@ -220,24 +219,21 @@ fn populate_data<C: Client + Clone + 'static>(
|
||||
game_id: GameId,
|
||||
) {
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
match client {
|
||||
Some(client) => {
|
||||
let overview = client.game_overview(&game_id).await.unwrap();
|
||||
state.dispatch(ViewStateAction::SetGame(overview));
|
||||
if let Some(client) = client {
|
||||
let overview = client.game_overview(&game_id).await.unwrap();
|
||||
state.dispatch(ViewStateAction::SetGame(overview));
|
||||
|
||||
let users = client.list_users().await.unwrap();
|
||||
state.dispatch(ViewStateAction::SetUsers(users));
|
||||
let users = client.list_users().await.unwrap();
|
||||
state.dispatch(ViewStateAction::SetUsers(users));
|
||||
|
||||
let characters = client
|
||||
.characters(CharacterFilter {
|
||||
game_id: Some(game_id),
|
||||
user_id: None,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
state.dispatch(ViewStateAction::SetCharacters(characters));
|
||||
}
|
||||
None => {}
|
||||
let characters = client
|
||||
.characters(CharacterFilter {
|
||||
game_id: Some(game_id),
|
||||
user_id: None,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
state.dispatch(ViewStateAction::SetCharacters(characters));
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -248,17 +244,14 @@ fn add_player<C: Client + Clone + 'static>(
|
||||
player_id: UserId,
|
||||
) {
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
match (client, (*state).overview.clone()) {
|
||||
(Some(client), Some(mut overview)) => {
|
||||
match client.link_player_to_game(&overview.id, &player_id).await {
|
||||
Ok(_) => {
|
||||
overview.players.insert(player_id);
|
||||
state.dispatch(ViewStateAction::SetGame(overview));
|
||||
}
|
||||
Err(err) => log!(format!("on_save failed: {:?}", err)),
|
||||
if let (Some(client), Some(mut overview)) = (client, state.overview.clone()) {
|
||||
match client.link_player_to_game(&overview.id, &player_id).await {
|
||||
Ok(_) => {
|
||||
overview.players.insert(player_id);
|
||||
state.dispatch(ViewStateAction::SetGame(overview));
|
||||
}
|
||||
Err(err) => log!(format!("on_save failed: {:?}", err)),
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -269,20 +262,17 @@ fn remove_player<C: Client + Clone + 'static>(
|
||||
player_id: UserId,
|
||||
) {
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
match (client, (*state).overview.clone()) {
|
||||
(Some(client), Some(mut overview)) => {
|
||||
match client
|
||||
.unlink_player_from_game(&overview.id, &player_id)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
let _ = overview.players.remove(&player_id);
|
||||
state.dispatch(ViewStateAction::SetGame(overview));
|
||||
}
|
||||
Err(err) => log!(format!("on_save failed: {:?}", err)),
|
||||
if let (Some(client), Some(mut overview)) = (client, state.overview.clone()) {
|
||||
match client
|
||||
.unlink_player_from_game(&overview.id, &player_id)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
let _ = overview.players.remove(&player_id);
|
||||
state.dispatch(ViewStateAction::SetGame(overview));
|
||||
}
|
||||
Err(err) => log!(format!("on_save failed: {:?}", err)),
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -292,14 +282,13 @@ fn create_character<C: Client + Clone + 'static>(
|
||||
state: UseReducerHandle<ViewState>,
|
||||
) {
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
match (client, (*state).overview.clone()) {
|
||||
(Some(client), Some(overview)) => match client.create_character(&overview.id).await {
|
||||
if let (Some(client), Some(overview)) = (client, state.overview.clone()) {
|
||||
match client.create_character(&overview.id).await {
|
||||
Ok(id) => {
|
||||
navigate_to(&format!("/characters/{}", id.as_str()));
|
||||
}
|
||||
Err(err) => log!(format!("create character failed: {:?}", err)),
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ pub struct GameViewProps {
|
||||
pub fn GameView(GameViewProps { game_id }: &GameViewProps) -> Html {
|
||||
let app_state: UseReducerHandle<AppState> = use_context().expect("app state not found");
|
||||
let game_state: UseStateHandle<Option<GameOverview>> = use_state(|| None);
|
||||
let _user_list: UseStateHandle<Vec<UserOverview>> = use_state(|| vec![]);
|
||||
let _user_list: UseStateHandle<Vec<UserOverview>> = use_state(std::vec::Vec::new);
|
||||
|
||||
let client = app_state.client().cloned();
|
||||
use_effect_with(
|
||||
@@ -84,12 +84,9 @@ fn request_game_overview(
|
||||
game_state: UseStateHandle<Option<GameOverview>>,
|
||||
) {
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
match client {
|
||||
Some(client) => match client.game_overview(&game_id).await {
|
||||
Ok(game) => game_state.set(Some(game)),
|
||||
Err(_) => todo!(),
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
if let Some(client) = client { match client.game_overview(&game_id).await {
|
||||
Ok(game) => game_state.set(Some(game)),
|
||||
Err(_) => todo!(),
|
||||
} }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use gloo_console::{error, log};
|
||||
use gloo_console::error;
|
||||
use stylist::css;
|
||||
use visions_types::{
|
||||
Card, CardId, GameId, GameMessage, GameOverview, GameRequest, Location, SceneId,
|
||||
@@ -185,7 +185,7 @@ impl Reducible for ViewState {
|
||||
ViewStateAction::CardNew => Self {
|
||||
sidebar_card: Some(Card {
|
||||
id: CardId::default(),
|
||||
game_id: (*self).game_id.clone(),
|
||||
game_id: self.game_id.clone(),
|
||||
location: Location::GM,
|
||||
title: Some("untitled".to_owned()),
|
||||
content: "".to_owned(),
|
||||
@@ -283,9 +283,8 @@ fn View(
|
||||
|
||||
let on_select_card = Callback::from(clone!(state, move |card_id| {
|
||||
let card_id = CardId::from(card_id);
|
||||
match state.cards.iter().find(|card| card.id == card_id) {
|
||||
Some(card) => state.dispatch(ViewStateAction::OpenCard(card.clone())),
|
||||
None => {}
|
||||
if let Some(card) = state.cards.iter().find(|card| card.id == card_id) {
|
||||
state.dispatch(ViewStateAction::OpenCard(card.clone()))
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -325,7 +324,7 @@ fn View(
|
||||
};
|
||||
|
||||
let mut characters = state.characters.clone();
|
||||
characters.sort_by(|a, b| a.cmp(b));
|
||||
characters.sort_by(|l, r| l.name().cmp(r.name()));
|
||||
|
||||
let pc_elements = characters
|
||||
.iter()
|
||||
@@ -597,6 +596,6 @@ fn move_card_to(
|
||||
let card = view_state.cards.iter().find(|c| c.id == card_id).cloned();
|
||||
if let Some(mut card) = card {
|
||||
card.location = dest;
|
||||
let _ = socket.send(GameRequest::CardUpdate(card));
|
||||
socket.send(GameRequest::CardUpdate(card));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use visions_types::*;
|
||||
use yew::prelude::*;
|
||||
|
||||
use crate::{
|
||||
client::{Client, ClientError, Connection},
|
||||
client::{Client, Connection},
|
||||
clone,
|
||||
components::*,
|
||||
systems::SystemName,
|
||||
@@ -13,12 +13,6 @@ use crate::{
|
||||
AppState,
|
||||
};
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct LandingProps {
|
||||
#[prop_or(Callback::from(|_| {}))]
|
||||
pub on_add_user: Callback<()>,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn Landing() -> Html {
|
||||
let app_state = use_context::<UseReducerHandle<AppState>>().expect("app state not found");
|
||||
@@ -47,9 +41,9 @@ struct GameListProps<C: Client + Clone> {
|
||||
|
||||
#[function_component]
|
||||
fn GameList<C: Client + Clone + 'static>(GameListProps { client }: &GameListProps<C>) -> Html {
|
||||
let game_list: UseStateHandle<Vec<GameOverview>> = use_state(|| vec![]);
|
||||
let game_list: UseStateHandle<Vec<GameOverview>> = use_state(std::vec::Vec::new);
|
||||
let user_ref: UseStateHandle<Option<UserOverview>> = use_state(|| None);
|
||||
let users_ref: UseStateHandle<Vec<UserOverview>> = use_state(|| vec![]);
|
||||
let users_ref: UseStateHandle<Vec<UserOverview>> = use_state(std::vec::Vec::new);
|
||||
let modal_title: UseStateHandle<String> = use_state(|| "".to_owned());
|
||||
let modal_content: UseStateHandle<Option<Html>> = use_state(|| None);
|
||||
|
||||
|
||||
@@ -223,7 +223,7 @@ async fn login_with_passkey() -> Result<(Connection, UserOverview), ClientError>
|
||||
.unwrap();
|
||||
|
||||
let credential: web_sys::PublicKeyCredential =
|
||||
web_sys::PublicKeyCredential::from(js_val).into();
|
||||
web_sys::PublicKeyCredential::from(js_val);
|
||||
let credential = webauthn_rs_proto::PublicKeyCredential::from(credential);
|
||||
|
||||
Connection::finish_auth_with_passkey(auth_response.auth_id, credential).await
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
use gloo_console::log;
|
||||
use stylist::css;
|
||||
use visions_types::{
|
||||
InvitationId, StartPasskeyAuthResponse, StartRegisterPasskeyRequest,
|
||||
InvitationId,
|
||||
StartRegisterPasskeyResponse,
|
||||
};
|
||||
use webauthn_rs_proto::RegisterPublicKeyCredential;
|
||||
use yew::prelude::*;
|
||||
|
||||
use crate::{
|
||||
client::{Client, ClientError, Connection},
|
||||
client::{ClientError, Connection},
|
||||
clone,
|
||||
components::*,
|
||||
design,
|
||||
state::AppState,
|
||||
utils::navigate_to,
|
||||
views::Login,
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Properties)]
|
||||
@@ -58,7 +56,7 @@ pub fn NewUser(NewUserProps { id }: &NewUserProps) -> Html {
|
||||
|
||||
log!(format!("NewUser: {:?} {:?}", is_valid, error));
|
||||
|
||||
let form = match ((*is_valid).clone(), (*error).clone()) {
|
||||
let form = match ((*is_valid), (*error).clone()) {
|
||||
(None, None) => html! { <div>{"Validating invitation"}</div> },
|
||||
(Some(true), None) => html! {
|
||||
<PanelElement title="Welcome to Visions VTT">
|
||||
|
||||
Reference in New Issue
Block a user