From ce440ae08c9762748eb89e81d6e33e269e5359a2 Mon Sep 17 00:00:00 2001 From: Adrian Wozniak Date: Thu, 28 May 2020 14:06:28 +0200 Subject: [PATCH] Add some styling to messages, parse description --- jirs-client/js/css/sidebar.css | 20 +++- jirs-client/js/css/styledIcon.css | 4 + jirs-client/src/project/update.rs | 6 +- jirs-client/src/shared/aside.rs | 1 + jirs-client/src/shared/navbar_left.rs | 120 ++++++++++++++++++++---- jirs-client/src/shared/styled_button.rs | 6 +- jirs-client/src/shared/styled_icon.rs | 2 + jirs-server/seed.sql | 13 ++- 8 files changed, 141 insertions(+), 31 deletions(-) diff --git a/jirs-client/js/css/sidebar.css b/jirs-client/js/css/sidebar.css index 8a6fc05e..7cd5e02f 100644 --- a/jirs-client/js/css/sidebar.css +++ b/jirs-client/js/css/sidebar.css @@ -122,4 +122,22 @@ nav#sidebar .linkItem > a > .linkText { font-family: var(--font-regular); } -.styledTooltip.messages > .messagesList > .message > .hyperlink {} +.styledTooltip.messages > .messagesList > .message > .hyperlink { + margin-top: 15px; +} + +.styledTooltip.messages > .messagesList > .message > .hyperlink > a { + color: var(--primary); +} + +.styledTooltip.messages > .messagesList > .message > .hyperlink > a > .styledIcon { + padding-right: 15px; +} + +.styledTooltip.messages > .messagesList > .message > .actions { + margin-top: 15px; +} + +.styledTooltip.messages > .messagesList > .message > .actions > .styledButton { + margin-right: 15px; +} diff --git a/jirs-client/js/css/styledIcon.css b/jirs-client/js/css/styledIcon.css index 77c8d7a9..ead6567f 100644 --- a/jirs-client/js/css/styledIcon.css +++ b/jirs-client/js/css/styledIcon.css @@ -176,3 +176,7 @@ i.styledIcon.user:before { i.styledIcon.message:before { content: "\efac"; } + +i.styledIcon.check:before { + content: "\ec4b"; +} diff --git a/jirs-client/src/project/update.rs b/jirs-client/src/project/update.rs index 414822ba..3e42f283 100644 --- a/jirs-client/src/project/update.rs +++ b/jirs-client/src/project/update.rs @@ -127,11 +127,7 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order fn init_load(model: &mut Model, orders: &mut impl Orders) { enqueue_ws_msg( - vec![ - WsMsg::ProjectIssuesRequest, - WsMsg::ProjectUsersRequest, - WsMsg::IssueStatusesRequest, - ], + vec![WsMsg::ProjectIssuesRequest, WsMsg::IssueStatusesRequest], model.ws.as_ref(), orders, ); diff --git a/jirs-client/src/shared/aside.rs b/jirs-client/src/shared/aside.rs index bc583639..2e11eac6 100644 --- a/jirs-client/src/shared/aside.rs +++ b/jirs-client/src/shared/aside.rs @@ -16,6 +16,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders) { WsMsg::UserProjectsLoad, WsMsg::ProjectsLoad, WsMsg::MessagesRequest, + WsMsg::ProjectUsersRequest, ], model.ws.as_ref(), orders, diff --git a/jirs-client/src/shared/navbar_left.rs b/jirs-client/src/shared/navbar_left.rs index 87eb4c44..0548e0b9 100644 --- a/jirs-client/src/shared/navbar_left.rs +++ b/jirs-client/src/shared/navbar_left.rs @@ -1,6 +1,6 @@ use seed::{prelude::*, *}; -use jirs_data::Message; +use jirs_data::{Message, MessageType}; use crate::model::Model; use crate::shared::styled_avatar::StyledAvatar; @@ -111,25 +111,9 @@ fn messages_tooltip_popup(model: &Model) -> Node { }); let mut messages: Vec> = vec![]; for (idx, message) in model.messages.iter().enumerate() { - let Message { - id: _, - receiver_id: _, - sender_id: _, - summary, - description, - message_type, - hyper_link, - created_at: _, - updated_at: _, - } = message; + let message_ui = message_ui(model, message); - messages.push(div![ - class!["message"], - attrs![At::Class => format!("{}", message_type)], - div![class!["summary"], summary], - div![class!["description"], description], - div![class!["hyperlink"], hyper_link], - ]); + messages.push(message_ui); if idx != model.messages.len() - 1 { messages.push(divider()); } @@ -144,6 +128,70 @@ fn messages_tooltip_popup(model: &Model) -> Node { .into_node() } +fn message_ui(model: &Model, message: &Message) -> Node { + let Message { + id: _, + receiver_id: _, + sender_id: _, + summary, + description, + message_type, + hyper_link, + created_at: _, + updated_at: _, + } = message; + + let hyperlink = if hyper_link.is_empty() { + empty![] + } else { + let link_icon = StyledIcon::build(Icon::Link).build().into_node(); + div![ + class!["hyperlink"], + a![attrs![At::Href => hyper_link], link_icon, hyper_link] + ] + }; + + let message_description = parse_description(model, description.as_str()); + + match message_type { + MessageType::ReceivedInvitation => { + let accept = StyledButton::build() + .primary() + .text("Accept") + .active(true) + .icon(Icon::Check) + .build() + .into_node(); + let reject = StyledButton::build() + .danger() + .text("Dismiss") + .icon(Icon::Close) + .active(true) + .build() + .into_node(); + div![ + class!["message"], + attrs![At::Class => format!("{}", message_type)], + div![class!["summary"], summary], + div![class!["description"], message_description], + div![class!["actions"], accept, reject], + ] + } + MessageType::AssignedToIssue => div![ + class!["message assignedToIssue"], + div![class!["summary"], summary], + div![class!["description"], message_description], + hyperlink, + ], + MessageType::Mention => div![ + class!["message mention"], + div![class!["summary"], summary], + div![class!["description"], message_description], + hyperlink, + ], + } +} + fn about_tooltip_popup(model: &Model) -> Node { let visit_website = StyledButton::build() .text("Visit Website".to_string()) @@ -207,3 +255,37 @@ fn about_tooltip_popup(model: &Model) -> Node { .build() .into_node() } + +fn parse_description(model: &Model, desc: &str) -> Node { + let mut container: Node = div![]; + for word in desc.split(' ') { + let child = parse_email(word) + .and_then(|email| { + model + .users + .iter() + .enumerate() + .find(|(_, user)| user.email == email) + }) + .map(|(index, user)| { + let avatar = StyledAvatar::build() + .avatar_url(user.avatar_url.as_ref().cloned().unwrap_or_default()) + .user_index(index) + .size(16) + .build() + .into_node(); + span![class!["mention"], avatar, user.name.as_str()] + }) + .unwrap_or_else(|| span![word]); + container.add_child(child).add_text(" "); + } + container +} + +fn parse_email(word: &str) -> Option<&str> { + if word.starts_with("@<") && word.ends_with(">") { + Some(&word[2..(word.len() - 1)]) + } else { + None + } +} diff --git a/jirs-client/src/shared/styled_button.rs b/jirs-client/src/shared/styled_button.rs index 31d0d4fa..34c3cd3b 100644 --- a/jirs-client/src/shared/styled_button.rs +++ b/jirs-client/src/shared/styled_button.rs @@ -52,9 +52,9 @@ impl StyledButtonBuilder { self.variant(Variant::Success) } - // pub fn danger(self) -> Self { - // self.variant(Variant::Danger) - // } + pub fn danger(self) -> Self { + self.variant(Variant::Danger) + } pub fn secondary(self) -> Self { self.variant(Variant::Secondary) diff --git a/jirs-client/src/shared/styled_icon.rs b/jirs-client/src/shared/styled_icon.rs index fb16ebab..349a5a25 100644 --- a/jirs-client/src/shared/styled_icon.rs +++ b/jirs-client/src/shared/styled_icon.rs @@ -30,6 +30,7 @@ pub enum Icon { Issues, Settings, Close, + Check, Feedback, Trash, Github, @@ -83,6 +84,7 @@ impl std::fmt::Display for Icon { Icon::Issues => "issues", Icon::Settings => "settings", Icon::Close => "close", + Icon::Check => "check", Icon::Feedback => "feedback", Icon::Trash => "trash", Icon::Github => "github", diff --git a/jirs-server/seed.sql b/jirs-server/seed.sql index 3dbff5b1..80929cab 100644 --- a/jirs-server/seed.sql +++ b/jirs-server/seed.sql @@ -178,12 +178,19 @@ VALUES ( 'Bar', 'Suspendisse tincidunt euismod justo, at porttitor dolor fermentum ut. Interdum et malesuada fames ac ante ipsum primis in faucibus. Suspendisse maximus sed ex ut sollicitudin. Etiam volutpat ultricies vehicula. Sed at est in mauris cursus fermentum. Duis et lacus metus. Sed ut egestas ipsum, ac consectetur metus. In felis diam, cursus eu felis non, tincidunt elementum lacus. Etiam et massa odio. Vestibulum ornare felis maximus facilisis semper.', 'assigned_to_issue', - '/issue/1', + '/issues/1', + now(), now() +), ( + 1, 1, + 'Foz Baz', + 'Suspendisse quam ligula, @ auctor vel diam sit amet, tincidunt venenatis justo. Vestibulum tincidunt mauris et est iaculis, vel consequat turpis porta. Integer eu urna quis diam pharetra lobortis vel nec lacus. Donec ac mollis risus. Morbi pellentesque pulvinar libero, sit amet finibus risus fermentum ac. Vivamus imperdiet mi congue ligula luctus condimentum. Duis arcu turpis, dignissim quis purus eget, dignissim elementum risus. Donec mattis rhoncus lorem quis blandit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec dignissim tellus eu cursus finibus. Ut pellentesque mi at eros maximus, eu tempor est sodales. Mauris vel feugiat ligula. Integer quis interdum velit, at iaculis arcu. Duis leo sapien, egestas eget erat id, fringilla pulvinar nulla. Nam sollicitudin ullamcorper finibus.', + 'mention', + '', now(), now() ), ( 2, 1, - 'Foz Baz', - 'Suspendisse quam ligula, @ auctor vel diam sit amet, tincidunt venenatis justo. Vestibulum tincidunt mauris et est iaculis, vel consequat turpis porta. Integer eu urna quis diam pharetra lobortis vel nec lacus. Donec ac mollis risus. Morbi pellentesque pulvinar libero, sit amet finibus risus fermentum ac. Vivamus imperdiet mi congue ligula luctus condimentum. Duis arcu turpis, dignissim quis purus eget, dignissim elementum risus. Donec mattis rhoncus lorem quis blandit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec dignissim tellus eu cursus finibus. Ut pellentesque mi at eros maximus, eu tempor est sodales. Mauris vel feugiat ligula. Integer quis interdum velit, at iaculis arcu. Duis leo sapien, egestas eget erat id, fringilla pulvinar nulla. Nam sollicitudin ullamcorper finibus.', + 'Hello world', + 'Suspendisse quam ligula, @ auctor vel diam sit amet, tincidunt venenatis justo. Vestibulum tincidunt mauris et est iaculis, vel consequat turpis porta. Integer eu urna quis diam pharetra lobortis vel nec lacus. Donec ac mollis risus. Morbi pellentesque pulvinar libero, sit amet finibus risus fermentum ac. Vivamus imperdiet mi congue ligula luctus condimentum. Duis arcu turpis, dignissim quis purus eget, dignissim elementum risus. Donec mattis rhoncus lorem quis blandit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec dignissim tellus eu cursus finibus. Ut pellentesque mi at eros maximus, eu tempor est sodales. Mauris vel feugiat ligula. Integer quis interdum velit, at iaculis arcu. Duis leo sapien, egestas eget erat id, fringilla pulvinar nulla. Nam sollicitudin ullamcorper finibus.', 'mention', '', now(), now()