Start display messages
This commit is contained in:
parent
92e129115a
commit
2d55c5f143
@ -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),
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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))) => {
|
||||||
|
@ -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(_))) => {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
|
@ -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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
|
@ -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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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) => {
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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) {
|
||||||
|
@ -736,4 +736,8 @@ pub enum WsMsg {
|
|||||||
|
|
||||||
// messages
|
// messages
|
||||||
Message(Message),
|
Message(Message),
|
||||||
|
MessagesRequest,
|
||||||
|
MessagesResponse(Vec<Message>),
|
||||||
|
MessageMarkSeen(MessageId),
|
||||||
|
MessageMarkedSeen(MessageId),
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user