Start display messages

This commit is contained in:
Adrian Woźniak 2020-05-20 21:44:58 +02:00
parent 92e129115a
commit 2d55c5f143
19 changed files with 373 additions and 108 deletions

View File

@ -1,8 +1,9 @@
use crate::FieldId; use seed::prelude::WebSocketMessage;
use jirs_data::{IssueId, IssueStatusId};
use jirs_data::{IssueId, IssueStatusId, WsMsg};
use crate::shared::styled_editor::Mode as TabMode; use crate::shared::styled_editor::Mode as TabMode;
use seed::prelude::WebSocketMessage; use crate::FieldId;
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum FieldChange { pub enum FieldChange {
@ -59,10 +60,11 @@ pub enum PageChanged {
#[derive(Debug)] #[derive(Debug)]
pub enum WebSocketChanged { pub enum WebSocketChanged {
WsMsg(jirs_data::WsMsg), WsMsg(WsMsg),
WebSocketMessage(WebSocketMessage), WebSocketMessage(WebSocketMessage),
WebSocketMessageLoaded(Vec<u8>), WebSocketMessageLoaded(Vec<u8>),
WebSocketOpened, WebSocketOpened,
WebSocketClosed, WebSocketClosed,
SendPing, SendPing,
Bounced(WsMsg),
} }

View File

@ -7,8 +7,9 @@ use jirs_data::*;
use crate::model::{ModalType, Model, Page}; use crate::model::{ModalType, Model, Page};
use crate::shared::styled_select::StyledSelectChange; use crate::shared::styled_select::StyledSelectChange;
use crate::shared::{go_to_board, go_to_login}; use crate::shared::styled_tooltip::Variant as StyledTooltip;
use crate::ws::{open_socket, read_incoming, send_ws_msg}; use crate::shared::{go_to_board, go_to_login, styled_tooltip};
use crate::ws::{flush_queue, open_socket, read_incoming, send_ws_msg};
mod changes; mod changes;
mod fields; mod fields;
@ -40,7 +41,7 @@ pub enum Msg {
StyledSelectChanged(FieldId, StyledSelectChange), StyledSelectChanged(FieldId, StyledSelectChange),
InternalFailure(String), InternalFailure(String),
ToggleAboutTooltip, ToggleTooltip(StyledTooltip),
// Auth Token // Auth Token
AuthTokenStored, AuthTokenStored,
@ -100,12 +101,13 @@ fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
Msg::WebSocketChange(change) => { Msg::WebSocketChange(change) => {
match change { match change {
WebSocketChanged::WebSocketOpened => { WebSocketChanged::WebSocketOpened => {
authorize_or_redirect(model); flush_queue(model, orders);
send_ws_msg(WsMsg::Ping, model.ws.as_ref()); send_ws_msg(WsMsg::Ping, model.ws.as_ref(), orders);
authorize_or_redirect(model, orders);
return; return;
} }
WebSocketChanged::SendPing => { WebSocketChanged::SendPing => {
send_ws_msg(WsMsg::Ping, model.ws.as_ref()); send_ws_msg(WsMsg::Ping, model.ws.as_ref(), orders);
return; return;
} }
WebSocketChanged::WebSocketMessage(incoming) => { WebSocketChanged::WebSocketMessage(incoming) => {
@ -135,6 +137,11 @@ fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
WebSocketChanged::WebSocketClosed => { WebSocketChanged::WebSocketClosed => {
open_socket(model, orders); open_socket(model, orders);
} }
WebSocketChanged::Bounced(ws_msg) => {
model.ws_queue.push(ws_msg);
open_socket(model, orders);
return;
}
}; };
Msg::WebSocketChange(change) Msg::WebSocketChange(change)
} }
@ -147,23 +154,24 @@ fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
match &msg { match &msg {
Msg::AuthTokenStored => { Msg::AuthTokenStored => {
go_to_board(); go_to_board(orders);
orders.skip().send_msg(Msg::ChangePage(Page::Project));
authorize_or_redirect(model);
return; return;
} }
Msg::AuthTokenErased => { Msg::AuthTokenErased => {
go_to_login(); go_to_login(orders);
orders.skip().send_msg(Msg::ChangePage(Page::SignIn));
authorize_or_redirect(model);
return; return;
} }
Msg::ChangePage(page) => { Msg::ChangePage(page) => {
model.page = *page; model.page = *page;
} }
Msg::ToggleAboutTooltip => { Msg::ToggleTooltip(variant) => match variant {
model.about_tooltip_visible = !model.about_tooltip_visible; styled_tooltip::Variant::About => {
} model.about_tooltip_visible = !model.about_tooltip_visible;
}
styled_tooltip::Variant::Messages => {
model.messages_tooltip_visible = !model.messages_tooltip_visible;
}
},
_ => (), _ => (),
} }
crate::modal::update(&msg, model, orders); crate::modal::update(&msg, model, orders);
@ -250,7 +258,6 @@ fn after_mount(url: Url, orders: &mut impl Orders<Msg>) -> AfterMount<Model> {
WS_URL = "".to_string(); WS_URL = "".to_string();
} }
model.page = resolve_page(url).unwrap_or_else(|| Page::Project); model.page = resolve_page(url).unwrap_or_else(|| Page::Project);
log!(model);
open_socket(&mut model, orders); open_socket(&mut model, orders);
AfterMount::new(model).url_handling(UrlHandling::PassToRoutes) AfterMount::new(model).url_handling(UrlHandling::PassToRoutes)
} }
@ -279,17 +286,17 @@ fn window_events(_model: &Model) -> Vec<EventHandler<Msg>> {
} }
#[inline] #[inline]
fn authorize_or_redirect(model: &Model) { fn authorize_or_redirect(model: &mut Model, orders: &mut impl Orders<Msg>) {
let pathname = seed::document().location().unwrap().pathname().unwrap();
match crate::shared::read_auth_token() { match crate::shared::read_auth_token() {
Ok(token) => { Ok(token) => {
send_ws_msg(WsMsg::AuthorizeRequest(token), model.ws.as_ref()); send_ws_msg(WsMsg::AuthorizeRequest(token), model.ws.as_ref(), orders);
} }
Err(..) => { Err(..) => {
let pathname = seed::document().location().unwrap().pathname().unwrap();
match pathname.as_str() { match pathname.as_str() {
"/login" | "/register" | "/invite" => {} "/login" | "/register" | "/invite" => {}
_ => { _ => {
go_to_login(); go_to_login(orders);
} }
}; };
} }

View File

@ -53,6 +53,7 @@ pub fn update(msg: &Msg, model: &mut crate::model::Model, orders: &mut impl Orde
send_ws_msg( send_ws_msg(
jirs_data::WsMsg::IssueCreateRequest(payload), jirs_data::WsMsg::IssueCreateRequest(payload),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::IssueCreated(issue))) => { Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::IssueCreated(issue))) => {

View File

@ -22,6 +22,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
crate::ws::send_ws_msg( crate::ws::send_ws_msg(
WsMsg::IssueStatusDelete(*issue_status_id), WsMsg::IssueStatusDelete(*issue_status_id),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::IssueStatusDeleted(_))) => { Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::IssueStatusDeleted(_))) => {

View File

@ -50,6 +50,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
PayloadVariant::IssueType(modal.payload.issue_type), PayloadVariant::IssueType(modal.payload.issue_type),
), ),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
Msg::StyledSelectChanged( Msg::StyledSelectChanged(
@ -64,6 +65,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
PayloadVariant::I32(modal.payload.issue_status_id), PayloadVariant::I32(modal.payload.issue_status_id),
), ),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
Msg::StyledSelectChanged( Msg::StyledSelectChanged(
@ -78,6 +80,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
PayloadVariant::I32(modal.payload.reporter_id), PayloadVariant::I32(modal.payload.reporter_id),
), ),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
Msg::StyledSelectChanged( Msg::StyledSelectChanged(
@ -92,6 +95,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
PayloadVariant::VecI32(modal.payload.user_ids.clone()), PayloadVariant::VecI32(modal.payload.user_ids.clone()),
), ),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
Msg::StyledSelectChanged( Msg::StyledSelectChanged(
@ -113,6 +117,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
PayloadVariant::VecI32(modal.payload.user_ids.clone()), PayloadVariant::VecI32(modal.payload.user_ids.clone()),
), ),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
Msg::StyledSelectChanged( Msg::StyledSelectChanged(
@ -127,6 +132,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
PayloadVariant::IssuePriority(modal.payload.priority), PayloadVariant::IssuePriority(modal.payload.priority),
), ),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
Msg::StrInputChanged( Msg::StrInputChanged(
@ -141,6 +147,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
PayloadVariant::String(modal.payload.title.clone()), PayloadVariant::String(modal.payload.title.clone()),
), ),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
Msg::StrInputChanged( Msg::StrInputChanged(
@ -163,6 +170,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
), ),
), ),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
// TimeSpent // TimeSpent
@ -178,6 +186,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
PayloadVariant::OptionI32(modal.payload.time_spent), PayloadVariant::OptionI32(modal.payload.time_spent),
), ),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
Msg::StyledSelectChanged( Msg::StyledSelectChanged(
@ -192,6 +201,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
PayloadVariant::OptionI32(modal.payload.time_spent), PayloadVariant::OptionI32(modal.payload.time_spent),
), ),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
// Time Remaining // Time Remaining
@ -207,6 +217,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
PayloadVariant::OptionI32(modal.payload.time_remaining), PayloadVariant::OptionI32(modal.payload.time_remaining),
), ),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
Msg::StyledSelectChanged( Msg::StyledSelectChanged(
@ -222,6 +233,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
PayloadVariant::OptionI32(modal.payload.time_remaining), PayloadVariant::OptionI32(modal.payload.time_remaining),
), ),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
// Estimate // Estimate
@ -237,6 +249,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
PayloadVariant::OptionI32(modal.payload.estimate), PayloadVariant::OptionI32(modal.payload.estimate),
), ),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
Msg::StyledSelectChanged( Msg::StyledSelectChanged(
@ -251,6 +264,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
PayloadVariant::OptionI32(modal.payload.estimate), PayloadVariant::OptionI32(modal.payload.estimate),
), ),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
Msg::ModalChanged(FieldChange::TabChanged( Msg::ModalChanged(FieldChange::TabChanged(
@ -290,7 +304,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
issue_id: modal.id, issue_id: modal.id,
}), }),
}; };
send_ws_msg(msg, model.ws.as_ref()); send_ws_msg(msg, model.ws.as_ref(), orders);
orders orders
.skip() .skip()
.send_msg(Msg::ModalChanged(FieldChange::ToggleCommentForm( .send_msg(Msg::ModalChanged(FieldChange::ToggleCommentForm(
@ -314,7 +328,11 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
modal.comment_form.creating = true; modal.comment_form.creating = true;
} }
Msg::DeleteComment(comment_id) => { Msg::DeleteComment(comment_id) => {
send_ws_msg(WsMsg::CommentDeleteRequest(*comment_id), model.ws.as_ref()); send_ws_msg(
WsMsg::CommentDeleteRequest(*comment_id),
model.ws.as_ref(),
orders,
);
orders.skip().send_msg(Msg::ModalDropped); orders.skip().send_msg(Msg::ModalDropped);
} }

View File

@ -19,8 +19,7 @@ pub fn update(msg: &Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>
match msg { match msg {
Msg::ModalDropped => match model.modals.pop() { Msg::ModalDropped => match model.modals.pop() {
Some(ModalType::EditIssue(..)) | Some(ModalType::AddIssue(..)) => { Some(ModalType::EditIssue(..)) | Some(ModalType::AddIssue(..)) => {
go_to_board(); go_to_board(orders);
orders.send_msg(Msg::ChangePage(Page::Project));
} }
_ => (), _ => (),
}, },
@ -40,14 +39,14 @@ pub fn update(msg: &Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::ProjectIssuesLoaded(_issues))) => { Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::ProjectIssuesLoaded(_issues))) => {
match model.page { match model.page {
Page::EditIssue(issue_id) if model.modals.is_empty() => { Page::EditIssue(issue_id) if model.modals.is_empty() => {
push_edit_modal(issue_id, model) push_edit_modal(issue_id, model, orders)
} }
_ => (), _ => (),
} }
} }
Msg::ChangePage(Page::EditIssue(issue_id)) => { Msg::ChangePage(Page::EditIssue(issue_id)) => {
push_edit_modal(*issue_id, model); push_edit_modal(*issue_id, model, orders);
} }
Msg::ChangePage(Page::AddIssue) => { Msg::ChangePage(Page::AddIssue) => {
@ -102,7 +101,7 @@ pub fn view(model: &model::Model) -> Node<Msg> {
section![id!["modals"], modals] section![id!["modals"], modals]
} }
fn push_edit_modal(issue_id: i32, model: &mut Model) { fn push_edit_modal(issue_id: i32, model: &mut Model, orders: &mut impl Orders<Msg>) {
let time_tracking_type = model let time_tracking_type = model
.project .project
.as_ref() .as_ref()
@ -118,6 +117,10 @@ fn push_edit_modal(issue_id: i32, model: &mut Model) {
Box::new(EditIssueModal::new(issue, time_tracking_type)), Box::new(EditIssueModal::new(issue, time_tracking_type)),
) )
}; };
send_ws_msg(WsMsg::IssueCommentsRequest(issue_id), model.ws.as_ref()); send_ws_msg(
WsMsg::IssueCommentsRequest(issue_id),
model.ws.as_ref(),
orders,
);
model.modals.push(modal); model.modals.push(modal);
} }

View File

@ -434,10 +434,12 @@ pub enum PageContent {
#[derive(Debug)] #[derive(Debug)]
pub struct Model { pub struct Model {
pub ws: Option<WebSocket>, pub ws: Option<WebSocket>,
pub ws_queue: Vec<WsMsg>,
pub host_url: String, pub host_url: String,
pub ws_url: String, pub ws_url: String,
pub access_token: Option<Uuid>, pub access_token: Option<Uuid>,
pub about_tooltip_visible: bool, pub about_tooltip_visible: bool,
pub messages_tooltip_visible: bool,
// mapped // mapped
pub comments_by_project_id: HashMap<ProjectId, Vec<Comment>>, pub comments_by_project_id: HashMap<ProjectId, Vec<Comment>>,
@ -467,6 +469,7 @@ impl Model {
pub fn new(host_url: String, ws_url: String) -> Self { pub fn new(host_url: String, ws_url: String) -> Self {
Self { Self {
ws: None, ws: None,
ws_queue: vec![],
access_token: None, access_token: None,
user: None, user: None,
issue_form: None, issue_form: None,
@ -483,6 +486,7 @@ impl Model {
project: None, project: None,
comments: vec![], comments: vec![],
about_tooltip_visible: false, about_tooltip_visible: false,
messages_tooltip_visible: false,
issue_statuses: vec![], issue_statuses: vec![],
messages: vec![Message { messages: vec![Message {
id: 0, id: 0,

View File

@ -14,15 +14,14 @@ use crate::ws::send_ws_msg;
use crate::{FieldId, Msg, PageChanged, ProfilePageChange, WebSocketChanged}; use crate::{FieldId, Msg, PageChanged, ProfilePageChange, WebSocketChanged};
pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) { pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) {
let user = match model.user {
Some(ref user) => user,
_ => return,
};
match msg { match msg {
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(..))) Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(..)))
| Msg::ChangePage(Page::Profile) => { | Msg::ChangePage(Page::Profile) => {
send_ws_msg(WsMsg::ProjectRequest, model.ws.as_ref()); send_ws_msg(WsMsg::ProjectRequest, model.ws.as_ref(), orders);
let user = match model.user {
Some(ref user) => user,
_ => return,
};
model.page_content = PageContent::Profile(Box::new(ProfilePage::new(user))); model.page_content = PageContent::Profile(Box::new(ProfilePage::new(user)));
} }
_ => (), _ => (),
@ -71,6 +70,7 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
profile_page.name.value.clone(), profile_page.name.value.clone(),
), ),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
_ => (), _ => (),

View File

@ -10,7 +10,7 @@ use crate::shared::styled_icon::{Icon, StyledIcon};
use crate::shared::styled_input::StyledInput; use crate::shared::styled_input::StyledInput;
use crate::shared::styled_select::StyledSelectChange; use crate::shared::styled_select::StyledSelectChange;
use crate::shared::{inner_layout, ToNode}; use crate::shared::{inner_layout, ToNode};
use crate::ws::send_ws_msg; use crate::ws::{enqueue_ws_msg, send_ws_msg};
use crate::{BoardPageChange, EditIssueModalSection, FieldId, Msg, PageChanged, WebSocketChanged}; use crate::{BoardPageChange, EditIssueModalSection, FieldId, Msg, PageChanged, WebSocketChanged};
pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) { pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) {
@ -37,10 +37,16 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
| Msg::ChangePage(Page::Project) | Msg::ChangePage(Page::Project)
| Msg::ChangePage(Page::AddIssue) | Msg::ChangePage(Page::AddIssue)
| Msg::ChangePage(Page::EditIssue(..)) => { | Msg::ChangePage(Page::EditIssue(..)) => {
send_ws_msg(jirs_data::WsMsg::ProjectRequest, model.ws.as_ref()); enqueue_ws_msg(
send_ws_msg(jirs_data::WsMsg::ProjectIssuesRequest, model.ws.as_ref()); vec![
send_ws_msg(jirs_data::WsMsg::ProjectUsersRequest, model.ws.as_ref()); jirs_data::WsMsg::ProjectRequest,
send_ws_msg(jirs_data::WsMsg::IssueStatusesRequest, model.ws.as_ref()); jirs_data::WsMsg::ProjectIssuesRequest,
jirs_data::WsMsg::ProjectUsersRequest,
jirs_data::WsMsg::IssueStatusesRequest,
],
model.ws.as_ref(),
orders,
);
} }
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::IssueUpdated(issue))) => { Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::IssueUpdated(issue))) => {
let mut old: Vec<Issue> = vec![]; let mut old: Vec<Issue> = vec![];
@ -108,7 +114,7 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
crate::ws::issue::drag_started(issue_id, model) crate::ws::issue::drag_started(issue_id, model)
} }
Msg::PageChanged(PageChanged::Board(BoardPageChange::IssueDragStopped(_))) => { Msg::PageChanged(PageChanged::Board(BoardPageChange::IssueDragStopped(_))) => {
crate::ws::issue::sync(model); crate::ws::issue::sync(model, orders);
} }
Msg::PageChanged(PageChanged::Board(BoardPageChange::ExchangePosition( Msg::PageChanged(PageChanged::Board(BoardPageChange::ExchangePosition(
issue_bellow_id, issue_bellow_id,
@ -117,7 +123,7 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
crate::ws::issue::change_status(status, model) crate::ws::issue::change_status(status, model)
} }
Msg::PageChanged(PageChanged::Board(BoardPageChange::IssueDropZone(_status))) => { Msg::PageChanged(PageChanged::Board(BoardPageChange::IssueDropZone(_status))) => {
crate::ws::issue::sync(model) crate::ws::issue::sync(model, orders)
} }
Msg::PageChanged(PageChanged::Board(BoardPageChange::DragLeave(_id))) => { Msg::PageChanged(PageChanged::Board(BoardPageChange::DragLeave(_id))) => {
project_page.issue_drag.clear_last(); project_page.issue_drag.clear_last();
@ -126,6 +132,7 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
send_ws_msg( send_ws_msg(
jirs_data::WsMsg::IssueDeleteRequest(issue_id), jirs_data::WsMsg::IssueDeleteRequest(issue_id),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
_ => (), _ => (),

View File

@ -1,3 +1,5 @@
use std::collections::HashSet;
use seed::{prelude::*, *}; use seed::{prelude::*, *};
use wasm_bindgen::__rt::std::collections::HashMap; use wasm_bindgen::__rt::std::collections::HashMap;
@ -18,7 +20,7 @@ use crate::shared::styled_input::StyledInput;
use crate::shared::styled_select::{StyledSelect, StyledSelectChange}; use crate::shared::styled_select::{StyledSelect, StyledSelectChange};
use crate::shared::styled_textarea::StyledTextarea; use crate::shared::styled_textarea::StyledTextarea;
use crate::shared::{inner_layout, ToChild, ToNode}; use crate::shared::{inner_layout, ToChild, ToNode};
use crate::ws::send_ws_msg; use crate::ws::{enqueue_ws_msg, send_ws_msg};
use crate::FieldChange::TabChanged; use crate::FieldChange::TabChanged;
use crate::{ use crate::{
model, FieldId, Msg, PageChanged, ProjectFieldId, ProjectPageChange, WebSocketChanged, model, FieldId, Msg, PageChanged, ProjectFieldId, ProjectPageChange, WebSocketChanged,
@ -389,9 +391,15 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
match msg { match msg {
Msg::WebSocketChange(ref change) => match change { Msg::WebSocketChange(ref change) => match change {
WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(..)) => { WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(..)) => {
send_ws_msg(WsMsg::ProjectRequest, model.ws.as_ref()); enqueue_ws_msg(
send_ws_msg(WsMsg::IssueStatusesRequest, model.ws.as_ref()); vec![
send_ws_msg(WsMsg::ProjectIssuesRequest, model.ws.as_ref()); WsMsg::ProjectRequest,
WsMsg::IssueStatusesRequest,
WsMsg::ProjectIssuesRequest,
],
model.ws.as_ref(),
orders,
);
} }
WebSocketChanged::WsMsg(WsMsg::ProjectLoaded(..)) => { WebSocketChanged::WsMsg(WsMsg::ProjectLoaded(..)) => {
build_page_content(model); build_page_content(model);
@ -409,9 +417,15 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
Msg::ChangePage(Page::ProjectSettings) => { Msg::ChangePage(Page::ProjectSettings) => {
build_page_content(model); build_page_content(model);
if model.user.is_some() { if model.user.is_some() {
send_ws_msg(WsMsg::ProjectRequest, model.ws.as_ref()); enqueue_ws_msg(
send_ws_msg(WsMsg::IssueStatusesRequest, model.ws.as_ref()); vec![
send_ws_msg(WsMsg::ProjectIssuesRequest, model.ws.as_ref()); WsMsg::ProjectRequest,
WsMsg::IssueStatusesRequest,
WsMsg::ProjectIssuesRequest,
],
model.ws.as_ref(),
orders,
);
} }
} }
_ => (), _ => (),
@ -465,6 +479,7 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
time_tracking: Some(page.time_tracking.value.into()), time_tracking: Some(page.time_tracking.value.into()),
}), }),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDragStarted( Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDragStarted(
@ -475,7 +490,7 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDragStopped( Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDragStopped(
_issue_status_id, _issue_status_id,
))) => { ))) => {
sync(model); sync(model, orders);
} }
Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDragLeave( Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDragLeave(
_issue_status_id, _issue_status_id,
@ -486,7 +501,7 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDropZone( Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDropZone(
_issue_status_id, _issue_status_id,
))) => { ))) => {
sync(model); sync(model, orders);
} }
Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::EditIssueStatusName( Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::EditIssueStatusName(
id, id,
@ -503,6 +518,7 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
send_ws_msg( send_ws_msg(
WsMsg::IssueStatusUpdate(id, name.to_string(), pos), WsMsg::IssueStatusUpdate(id, name.to_string(), pos),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
} }
@ -525,7 +541,7 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
let name = page.name.value.clone(); let name = page.name.value.clone();
let position = model.issue_statuses.len(); let position = model.issue_statuses.len();
let ws_msg = WsMsg::IssueStatusCreate(name, position as i32); let ws_msg = WsMsg::IssueStatusCreate(name, position as i32);
send_ws_msg(ws_msg, model.ws.as_ref()); send_ws_msg(ws_msg, model.ws.as_ref(), orders);
} }
_ => (), _ => (),
} }
@ -581,20 +597,25 @@ fn exchange_position(bellow_id: IssueStatusId, model: &mut Model) {
page.column_drag.last_id = Some(bellow_id); page.column_drag.last_id = Some(bellow_id);
} }
fn sync(model: &mut Model) { fn sync(model: &mut Model, orders: &mut impl Orders<Msg>) {
let page = match &mut model.page_content { let dirty = match &mut model.page_content {
PageContent::ProjectSettings(page) => page, PageContent::ProjectSettings(page) => {
let mut old = HashSet::new();
std::mem::swap(&mut old, &mut page.column_drag.dirty);
old
}
_ => return error!("bad content type"), _ => return error!("bad content type"),
}; };
for id in page.column_drag.dirty.iter() { for id in dirty {
let IssueStatus { name, position, .. } = let IssueStatus { name, position, .. } =
match model.issue_statuses.iter().find(|is| is.id == *id) { match model.issue_statuses.iter().find(|is| is.id == id) {
Some(is) => is, Some(is) => is,
_ => continue, _ => continue,
}; };
send_ws_msg( send_ws_msg(
WsMsg::IssueStatusUpdate(*id, name.clone(), *position), WsMsg::IssueStatusUpdate(id, name.clone(), *position),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
} }

View File

@ -1,9 +1,11 @@
use seed::{prelude::*, *};
use std::str::FromStr; use std::str::FromStr;
use seed::{prelude::*, *};
use jirs_data::*; use jirs_data::*;
use crate::model::Model; use crate::model::Model;
use crate::model::Page;
use crate::Msg; use crate::Msg;
pub mod aside; pub mod aside;
@ -33,12 +35,14 @@ pub trait ToChild {
fn to_child(&self) -> Self::Builder; fn to_child(&self) -> Self::Builder;
} }
pub fn go_to_board() { pub fn go_to_board(orders: &mut impl Orders<Msg>) {
go_to("/board"); go_to("/board");
orders.skip().send_msg(Msg::ChangePage(Page::Project));
} }
pub fn go_to_login() { pub fn go_to_login(orders: &mut impl Orders<Msg>) {
go_to("/login"); go_to("/login");
orders.skip().send_msg(Msg::ChangePage(Page::SignIn));
} }
pub fn go_to(url: &str) { pub fn go_to(url: &str) {

View File

@ -1,5 +1,7 @@
use seed::{prelude::*, *}; use seed::{prelude::*, *};
use jirs_data::Message;
use crate::model::Model; use crate::model::Model;
use crate::shared::styled_avatar::StyledAvatar; use crate::shared::styled_avatar::StyledAvatar;
use crate::shared::styled_button::StyledButton; use crate::shared::styled_button::StyledButton;
@ -42,49 +44,102 @@ pub fn render(model: &Model) -> Vec<Node<Msg>> {
let messages = if model.messages.is_empty() { let messages = if model.messages.is_empty() {
empty![] empty![]
} else { } else {
navbar_left_item("Messages", Icon::Message, None) navbar_left_item(
"Messages",
Icon::Message,
None,
Some(mouse_ev(Ev::Click, |ev| {
ev.prevent_default();
Msg::ToggleTooltip(styled_tooltip::Variant::Messages)
})),
)
}; };
vec![ vec![
about_tooltip_popup(model), about_tooltip_popup(model),
messages_tooltip_popup(model),
aside![ aside![
id!["navbar-left"], id!["navbar-left"],
a![ a![
attrs![At::Class => "logoLink", At::Href => "/"], class!["logoLink"],
div![attrs![At::Class => "styledLogo"], logo_svg] attrs![At::Href => "/"],
div![class!["styledLogo"], logo_svg]
], ],
navbar_left_item("Search issues", Icon::Search, None), navbar_left_item("Search issues", Icon::Search, None, None),
navbar_left_item("Create Issue", Icon::Plus, Some("/add-issue")), navbar_left_item("Create Issue", Icon::Plus, Some("/add-issue"), None),
div![ div![
class!["bottom"], class!["bottom"],
navbar_left_item("Profile", user_icon, Some("/profile")), navbar_left_item("Profile", user_icon, Some("/profile"), None),
messages, messages,
about_tooltip(model, navbar_left_item("About", Icon::Help, None)), about_tooltip(model, navbar_left_item("About", Icon::Help, None, None)),
], ],
], ],
] ]
} }
fn navbar_left_item<I>(text: &str, icon: I, href: Option<&str>) -> Node<Msg> fn navbar_left_item<I>(
text: &str,
icon: I,
href: Option<&str>,
on_click: Option<EventHandler<Msg>>,
) -> Node<Msg>
where where
I: IntoNavItemIcon, I: IntoNavItemIcon,
{ {
let styled_icon = icon.into_nav_item_icon(); let styled_icon = icon.into_nav_item_icon();
let href = href.unwrap_or_else(|| "#"); let href = href.unwrap_or_else(|| "#");
a![ a![
class!["item"], class!["item"],
attrs![At::Href => href], attrs![At::Href => href],
styled_icon, styled_icon,
span![class!["itemText"], text] span![class!["itemText"], text],
on_click,
] ]
} }
pub fn about_tooltip(_model: &Model, children: Node<Msg>) -> Node<Msg> { pub fn about_tooltip(_model: &Model, children: Node<Msg>) -> Node<Msg> {
div![ let on_click: EventHandler<Msg> = ev(Ev::Click, move |_| {
attrs![At::Class => "aboutTooltip"], Some(Msg::ToggleTooltip(styled_tooltip::Variant::About))
ev(Ev::Click, |_| Msg::ToggleAboutTooltip), });
children div![class!["aboutTooltip"], on_click, children]
] }
fn messages_tooltip_popup(model: &Model) -> Node<Msg> {
let on_click: EventHandler<Msg> = ev(Ev::Click, move |_| {
Some(Msg::ToggleTooltip(styled_tooltip::Variant::Messages))
});
let messages: Vec<Node<Msg>> = model
.messages
.iter()
.map(|message| {
let Message {
id: _,
receiver_id: _,
sender_id: _,
summary,
description,
message_type,
hyper_link,
created_at: _,
updated_at: _,
} = message;
div![
class!["message"],
class![message_type.as_str()],
div![class!["summary"], summary],
div![class!["description"], description],
div![class!["hyperlink"], hyper_link],
]
})
.collect();
let body = div![on_click, class!["messagesList"], messages];
styled_tooltip::StyledTooltip::build()
.visible(model.messages_tooltip_visible)
.messages_tooltip()
.add_child(body)
.build()
.into_node()
} }
fn about_tooltip_popup(model: &Model) -> Node<Msg> { fn about_tooltip_popup(model: &Model) -> Node<Msg> {
@ -100,23 +155,23 @@ fn about_tooltip_popup(model: &Model) -> Node<Msg> {
.build() .build()
.into_node(); .into_node();
styled_tooltip::StyledTooltip { let on_click = mouse_ev(Ev::Click, |_| {
visible: model.about_tooltip_visible, Msg::ToggleTooltip(styled_tooltip::Variant::About)
class_name: "aboutTooltipPopup".to_string(), });
children: div![ let body = div![
ev(Ev::Click, |_| Msg::ToggleAboutTooltip), on_click,
attrs![At::Class => "feedbackDropdown"], class!["feedbackDropdown"],
div![ div![
attrs![At::Class => "feedbackImageCont"], class!["feedbackImageCont"],
img![attrs![At::Src => "/feedback.png"]], img![attrs![At::Src => "/feedback.png"]],
class!["feedbackImage"], class!["feedbackImage"],
], ],
div![ div![
attrs![At::Class => "feedbackParagraph"], class!["feedbackParagraph"],
"This simplified Jira clone is built with Seed.rs on the front-end and Actix-Web on the back-end." "This simplified Jira clone is built with Seed.rs on the front-end and Actix-Web on the back-end."
], ],
div![ div![
attrs![At::Class => "feedbackParagraph"], class!["feedbackParagraph"],
"Read more on my website or reach out via ", "Read more on my website or reach out via ",
a![ a![
attrs![At::Href => "mailto:adrian.wozniak@ita-prog.pl"], attrs![At::Href => "mailto:adrian.wozniak@ita-prog.pl"],
@ -140,6 +195,13 @@ fn about_tooltip_popup(model: &Model) -> Node<Msg> {
], ],
github_repo github_repo
] ]
], ];
}.into_node()
styled_tooltip::StyledTooltip::build()
.visible(model.about_tooltip_visible)
.about_tooltip()
.add_class("aboutTooltipPopup")
.add_child(body)
.build()
.into_node()
} }

View File

@ -3,10 +3,32 @@ use seed::{prelude::*, *};
use crate::shared::ToNode; use crate::shared::ToNode;
use crate::Msg; use crate::Msg;
#[derive(Debug, Copy, Clone)]
pub enum Variant {
About,
Messages,
}
impl Default for Variant {
fn default() -> Self {
Variant::Messages
}
}
impl std::fmt::Display for Variant {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Variant::About => f.write_str("about"),
Variant::Messages => f.write_str("messages"),
}
}
}
pub struct StyledTooltip { pub struct StyledTooltip {
pub visible: bool, visible: bool,
pub class_name: String, class_name: String,
pub children: Node<Msg>, children: Vec<Node<Msg>>,
variant: Variant,
} }
impl ToNode for StyledTooltip { impl ToNode for StyledTooltip {
@ -15,15 +37,69 @@ impl ToNode for StyledTooltip {
} }
} }
impl StyledTooltip {
pub fn build() -> StyledTooltipBuilder {
StyledTooltipBuilder::default()
}
}
#[derive(Default)]
pub struct StyledTooltipBuilder {
visible: bool,
class_list: Vec<String>,
children: Vec<Node<Msg>>,
variant: Variant,
}
impl StyledTooltipBuilder {
pub fn visible(mut self, b: bool) -> Self {
self.visible = b;
self
}
pub fn add_class<S>(mut self, name: S) -> Self
where
S: Into<String>,
{
self.class_list.push(name.into());
self
}
pub fn add_child(mut self, child: Node<Msg>) -> Self {
self.children.push(child);
self
}
pub fn about_tooltip(mut self) -> Self {
self.variant = Variant::About;
self
}
pub fn messages_tooltip(mut self) -> Self {
self.variant = Variant::Messages;
self
}
pub fn build(self) -> StyledTooltip {
StyledTooltip {
visible: self.visible,
class_name: self.class_list.join(" "),
children: self.children,
variant: self.variant,
}
}
}
pub fn render(values: StyledTooltip) -> Node<Msg> { pub fn render(values: StyledTooltip) -> Node<Msg> {
let StyledTooltip { let StyledTooltip {
visible, visible,
class_name, class_name,
children, children,
variant,
} = values; } = values;
if visible { if visible {
div![ div![
attrs![At::Class => format!("styledTooltip {}", class_name)], attrs![At::Class => format!("styledTooltip {} {}", class_name, variant)],
children children
] ]
} else { } else {

View File

@ -52,6 +52,7 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
send_ws_msg( send_ws_msg(
WsMsg::AuthenticateRequest(page.email.clone(), page.username.clone()), WsMsg::AuthenticateRequest(page.email.clone(), page.username.clone()),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
Msg::BindClientRequest => { Msg::BindClientRequest => {
@ -62,7 +63,7 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
return; return;
} }
}; };
send_ws_msg(WsMsg::BindTokenCheck(bind_token), model.ws.as_ref()); send_ws_msg(WsMsg::BindTokenCheck(bind_token), model.ws.as_ref(), orders);
} }
Msg::WebSocketChange(change) => match change { Msg::WebSocketChange(change) => match change {
WebSocketChanged::WsMsg(WsMsg::AuthenticateSuccess) => { WebSocketChanged::WsMsg(WsMsg::AuthenticateSuccess) => {

View File

@ -14,7 +14,7 @@ use crate::validations::is_email;
use crate::ws::send_ws_msg; use crate::ws::send_ws_msg;
use crate::{model, FieldId, Msg, WebSocketChanged}; use crate::{model, FieldId, Msg, WebSocketChanged};
pub fn update(msg: Msg, model: &mut model::Model, _orders: &mut impl Orders<Msg>) { pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
if model.page != Page::SignUp { if model.page != Page::SignUp {
return; return;
} }
@ -45,6 +45,7 @@ pub fn update(msg: Msg, model: &mut model::Model, _orders: &mut impl Orders<Msg>
send_ws_msg( send_ws_msg(
WsMsg::SignUpRequest(page.email.clone(), page.username.clone()), WsMsg::SignUpRequest(page.email.clone(), page.username.clone()),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
Msg::WebSocketChange(change) => match change { Msg::WebSocketChange(change) => match change {

View File

@ -10,7 +10,7 @@ use crate::shared::styled_input::StyledInput;
use crate::shared::styled_select::*; use crate::shared::styled_select::*;
use crate::shared::{inner_layout, ToChild, ToNode}; use crate::shared::{inner_layout, ToChild, ToNode};
use crate::validations::is_email; use crate::validations::is_email;
use crate::ws::send_ws_msg; use crate::ws::{enqueue_ws_msg, send_ws_msg};
use crate::{FieldId, Msg, PageChanged, UsersPageChange, WebSocketChanged}; use crate::{FieldId, Msg, PageChanged, UsersPageChange, WebSocketChanged};
pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) { pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
@ -28,13 +28,19 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
match msg { match msg {
Msg::ChangePage(Page::Users) if model.user.is_some() => { Msg::ChangePage(Page::Users) if model.user.is_some() => {
send_ws_msg(WsMsg::InvitationListRequest, model.ws.as_ref()); enqueue_ws_msg(
send_ws_msg(WsMsg::InvitedUsersRequest, model.ws.as_ref()); vec![WsMsg::InvitationListRequest, WsMsg::InvitedUsersRequest],
model.ws.as_ref(),
orders,
);
} }
Msg::WebSocketChange(change) => match change { Msg::WebSocketChange(change) => match change {
WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(Ok(_))) if model.user.is_some() => { WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(Ok(_))) if model.user.is_some() => {
send_ws_msg(WsMsg::InvitationListRequest, model.ws.as_ref()); enqueue_ws_msg(
send_ws_msg(WsMsg::InvitedUsersRequest, model.ws.as_ref()); vec![WsMsg::InvitationListRequest, WsMsg::InvitedUsersRequest],
model.ws.as_ref(),
orders,
);
} }
WebSocketChanged::WsMsg(WsMsg::InvitedUsersLoaded(users)) => { WebSocketChanged::WsMsg(WsMsg::InvitedUsersLoaded(users)) => {
page.invited_users = users; page.invited_users = users;
@ -51,7 +57,7 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
} }
page.invitations.push(invitation); page.invitations.push(invitation);
} }
send_ws_msg(WsMsg::InvitationListRequest, model.ws.as_ref()); send_ws_msg(WsMsg::InvitationListRequest, model.ws.as_ref(), orders);
} }
WebSocketChanged::WsMsg(WsMsg::InvitedUserRemoveSuccess(email)) => { WebSocketChanged::WsMsg(WsMsg::InvitedUserRemoveSuccess(email)) => {
let mut old = vec![]; let mut old = vec![];
@ -63,7 +69,7 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
} }
} }
WebSocketChanged::WsMsg(WsMsg::InvitationSendSuccess) => { WebSocketChanged::WsMsg(WsMsg::InvitationSendSuccess) => {
send_ws_msg(WsMsg::InvitationListRequest, model.ws.as_ref()); send_ws_msg(WsMsg::InvitationListRequest, model.ws.as_ref(), orders);
page.form_state = InvitationFormState::Succeed; page.form_state = InvitationFormState::Succeed;
} }
WebSocketChanged::WsMsg(WsMsg::InvitationSendFailure) => { WebSocketChanged::WsMsg(WsMsg::InvitationSendFailure) => {
@ -102,16 +108,22 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
email: page.email.clone(), email: page.email.clone(),
}, },
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
Msg::InviteRevokeRequest(invitation_id) => { Msg::InviteRevokeRequest(invitation_id) => {
send_ws_msg( send_ws_msg(
WsMsg::InvitationRevokeRequest(invitation_id), WsMsg::InvitationRevokeRequest(invitation_id),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
Msg::InvitedUserRemove(email) => { Msg::InvitedUserRemove(email) => {
send_ws_msg(WsMsg::InvitedUserRemoveRequest(email), model.ws.as_ref()); send_ws_msg(
WsMsg::InvitedUserRemoveRequest(email),
model.ws.as_ref(),
orders,
);
} }
_ => (), _ => (),
} }

View File

@ -1,9 +1,11 @@
use seed::prelude::Orders;
use seed::*; use seed::*;
use jirs_data::*; use jirs_data::*;
use crate::model::{Model, PageContent}; use crate::model::{Model, PageContent};
use crate::ws::send_ws_msg; use crate::ws::send_ws_msg;
use crate::Msg;
pub fn drag_started(issue_id: IssueId, model: &mut Model) { pub fn drag_started(issue_id: IssueId, model: &mut Model) {
let project_page = match &mut model.page_content { let project_page = match &mut model.page_content {
@ -76,7 +78,7 @@ pub fn exchange_position(issue_bellow_id: IssueId, model: &mut Model) {
project_page.issue_drag.last_id = Some(issue_bellow_id); project_page.issue_drag.last_id = Some(issue_bellow_id);
} }
pub fn sync(model: &mut Model) { pub fn sync(model: &mut Model, orders: &mut impl Orders<Msg>) {
// log!("------------------------------------------------------------------"); // log!("------------------------------------------------------------------");
// log!("| SYNC |"); // log!("| SYNC |");
// log!("------------------------------------------------------------------"); // log!("------------------------------------------------------------------");
@ -97,6 +99,7 @@ pub fn sync(model: &mut Model) {
PayloadVariant::I32(issue.issue_status_id), PayloadVariant::I32(issue.issue_status_id),
), ),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
send_ws_msg( send_ws_msg(
WsMsg::IssueUpdateRequest( WsMsg::IssueUpdateRequest(
@ -105,6 +108,7 @@ pub fn sync(model: &mut Model) {
PayloadVariant::I32(issue.list_position), PayloadVariant::I32(issue.list_position),
), ),
model.ws.as_ref(), model.ws.as_ref(),
orders,
); );
} }
project_page.issue_drag.clear(); project_page.issue_drag.clear();

View File

@ -3,15 +3,41 @@ use seed::prelude::*;
use jirs_data::WsMsg; use jirs_data::WsMsg;
use crate::model::*; use crate::model::*;
use crate::shared::write_auth_token; use crate::shared::{go_to_board, write_auth_token};
use crate::{Msg, WebSocketChanged}; use crate::{Msg, WebSocketChanged};
pub mod issue; pub mod issue;
pub fn send_ws_msg(msg: WsMsg, ws: Option<&WebSocket>) { pub fn flush_queue(model: &mut Model, orders: &mut impl Orders<Msg>) {
use seed::browser::web_socket::State;
match model.ws.as_ref() {
Some(ws) if ws.state() != State::Open => return,
None => return,
_ => (),
};
let mut old = vec![];
std::mem::swap(&mut model.ws_queue, &mut old);
for msg in old {
send_ws_msg(msg, model.ws.as_ref(), orders);
}
}
pub fn enqueue_ws_msg(v: Vec<WsMsg>, ws: Option<&WebSocket>, orders: &mut impl Orders<Msg>) {
for msg in v {
send_ws_msg(msg, ws.clone(), orders);
}
}
pub fn send_ws_msg(msg: WsMsg, ws: Option<&WebSocket>, orders: &mut impl Orders<Msg>) {
use seed::browser::web_socket::State;
let ws = match ws { let ws = match ws {
Some(ws) => ws, Some(ws) if ws.state() == State::Open => ws,
_ => return, _ => {
orders
.skip()
.send_msg(Msg::WebSocketChange(WebSocketChanged::Bounced(msg)));
return;
}
}; };
let binary = bincode::serialize(&msg).unwrap(); let binary = bincode::serialize(&msg).unwrap();
ws.send_bytes(binary.as_slice()) ws.send_bytes(binary.as_slice())
@ -19,6 +45,16 @@ pub fn send_ws_msg(msg: WsMsg, ws: Option<&WebSocket>) {
} }
pub fn open_socket(model: &mut Model, orders: &mut impl Orders<Msg>) { pub fn open_socket(model: &mut Model, orders: &mut impl Orders<Msg>) {
use seed::browser::web_socket::State;
use seed::{prelude::*, *};
log!(model.ws.as_ref().map(|ws| ws.state()));
match model.ws.as_ref() {
Some(ws) if ws.state() != State::Closed => {
return;
}
_ => (),
};
if model.host_url.is_empty() { if model.host_url.is_empty() {
return; return;
} }
@ -43,6 +79,7 @@ pub fn update(msg: &WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
// auth // auth
WsMsg::AuthorizeLoaded(Ok(user)) => { WsMsg::AuthorizeLoaded(Ok(user)) => {
model.user = Some(user.clone()); model.user = Some(user.clone());
go_to_board(orders);
} }
WsMsg::AuthorizeExpired => { WsMsg::AuthorizeExpired => {
if let Ok(msg) = write_auth_token(None) { if let Ok(msg) = write_auth_token(None) {

View File

@ -736,4 +736,8 @@ pub enum WsMsg {
// messages // messages
Message(Message), Message(Message),
MessagesRequest,
MessagesResponse(Vec<Message>),
MessageMarkSeen(MessageId),
MessageMarkedSeen(MessageId),
} }