From f1c5fc4fe642fa8d88e6db01f4d6c6dda7e102b4 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 7 Oct 2021 21:50:08 +0530 Subject: [PATCH] feat: Shareable link for comments and communications - Also, highlight comment/communication that has been shared. --- .../js/frappe/form/footer/base_timeline.js | 8 +++-- .../js/frappe/form/footer/form_timeline.js | 36 ++++++++++++------- frappe/public/js/frappe/form/form.js | 10 +++++- .../form/templates/timeline_message_box.html | 14 ++++++++ frappe/public/js/frappe/utils/utils.js | 16 +++++++-- frappe/public/scss/common/css_variables.scss | 2 ++ frappe/public/scss/desk/dark.scss | 2 ++ frappe/public/scss/desk/global.scss | 13 +++++++ frappe/public/scss/desk/timeline.scss | 2 +- 9 files changed, 84 insertions(+), 19 deletions(-) diff --git a/frappe/public/js/frappe/form/footer/base_timeline.js b/frappe/public/js/frappe/form/footer/base_timeline.js index 702d964442..beeba16459 100644 --- a/frappe/public/js/frappe/form/footer/base_timeline.js +++ b/frappe/public/js/frappe/form/footer/base_timeline.js @@ -97,9 +97,13 @@ class BaseTimeline { } timeline_item.append(`
`); - timeline_item.find('.timeline-content').append(item.content); + let timeline_content = timeline_item.find('.timeline-content'); + timeline_content.append(item.content); if (!item.hide_timestamp && !item.is_card) { - timeline_item.find('.timeline-content').append(` - ${comment_when(item.creation)}`); + timeline_content.append(` - ${comment_when(item.creation)}`); + } + if (item.id) { + timeline_content.attr("id", item.id); } return timeline_item; } diff --git a/frappe/public/js/frappe/form/footer/form_timeline.js b/frappe/public/js/frappe/form/footer/form_timeline.js index b3feae3ee8..128bd355ad 100644 --- a/frappe/public/js/frappe/form/footer/form_timeline.js +++ b/frappe/public/js/frappe/form/footer/form_timeline.js @@ -96,6 +96,7 @@ class FormTimeline extends BaseTimeline { render_timeline_items() { super.render_timeline_items(); this.set_document_info(); + frappe.utils.bind_actions_with_object(this.timeline_items_wrapper, this); } set_document_info() { @@ -179,6 +180,7 @@ class FormTimeline extends BaseTimeline { is_card: true, content: this.get_communication_timeline_content(communication), doctype: "Communication", + id: `communication-${communication.name}`, name: communication.name }); }); @@ -246,6 +248,7 @@ class FormTimeline extends BaseTimeline { creation: comment.creation, is_card: true, doctype: "Comment", + id: `comment-${comment.name}`, name: comment.name, content: this.get_comment_timeline_content(comment), }; @@ -394,7 +397,7 @@ class FormTimeline extends BaseTimeline { } setup_reply(communication_box, communication_doc) { - let actions = communication_box.find('.actions'); + let actions = communication_box.find('.custom-actions'); let reply = $(`${frappe.utils.icon('reply', 'md')}`).click(() => { this.compose_mail(communication_doc); }); @@ -446,14 +449,16 @@ class FormTimeline extends BaseTimeline { let edit_wrapper = $(`
`).hide(); let edit_box = this.make_editable(edit_wrapper); let content_wrapper = comment_wrapper.find('.content'); - - let delete_button = $(); + let more_actions_wrapper = comment_wrapper.find('.more-actions'); if (frappe.model.can_delete("Comment")) { - delete_button = $(` - + const delete_option = $(` +
  • + + ${__("Delete")} + +
  • `).click(() => this.delete_comment(doc.name)); + more_actions_wrapper.find('.dropdown-menu').append(delete_option); } let dismiss_button = $(` @@ -493,15 +498,14 @@ class FormTimeline extends BaseTimeline { edit_button.toggle_edit_mode = () => { edit_button.edit_mode = !edit_button.edit_mode; edit_button.text(edit_button.edit_mode ? __('Save') : __('Edit')); - delete_button.toggle(!edit_button.edit_mode); + more_actions_wrapper.toggle(!edit_button.edit_mode); dismiss_button.toggle(edit_button.edit_mode); edit_wrapper.toggle(edit_button.edit_mode); content_wrapper.toggle(!edit_button.edit_mode); }; - - comment_wrapper.find('.actions').append(edit_button); - comment_wrapper.find('.actions').append(dismiss_button); - comment_wrapper.find('.actions').append(delete_button); + let actions_wrapper = comment_wrapper.find('.custom-actions'); + actions_wrapper.append(edit_button); + actions_wrapper.append(dismiss_button); } make_editable(container) { @@ -559,6 +563,14 @@ class FormTimeline extends BaseTimeline { }); }); } + + copy_link(ev) { + let doc_link = frappe.urllib.get_full_url( + frappe.utils.get_form_link(this.frm.doctype, this.frm.docname) + ); + let element_id = $(ev.currentTarget).closest(".timeline-content").attr("id"); + frappe.utils.copy_to_clipboard(`${doc_link}#${element_id}`); + } } export default FormTimeline; diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 97473c3069..a095956dfe 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -480,7 +480,11 @@ frappe.ui.form.Form = class FrappeForm { this.layout.show_empty_form_message(); } - this.scroll_to_element(); + frappe.after_ajax(() => { + $(document).ready(() => { + this.scroll_to_element(); + }); + }); } set_first_tab_as_active() { @@ -598,6 +602,8 @@ frappe.ui.form.Form = class FrappeForm { this.validate_form_action(save_action, resolve); var after_save = function(r) { + // to remove hash from URL to avoid scroll after save + history.replaceState(null, null, ' '); if(!r.exc) { if (["Save", "Update", "Amend"].indexOf(save_action)!==-1) { frappe.utils.play_sound("click"); @@ -1195,6 +1201,8 @@ frappe.ui.form.Form = class FrappeForm { if (selector.length) { frappe.utils.scroll_to(selector); } + } else if (window.location.hash && $(window.location.hash).length) { + frappe.utils.scroll_to(window.location.hash, true, 200, null, null, true); } } diff --git a/frappe/public/js/frappe/form/templates/timeline_message_box.html b/frappe/public/js/frappe/form/templates/timeline_message_box.html index 3884918165..a38e36c7b5 100644 --- a/frappe/public/js/frappe/form/templates/timeline_message_box.html +++ b/frappe/public/js/frappe/form/templates/timeline_message_box.html @@ -63,6 +63,20 @@ {% } %} +
    +
    diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index f534dff1c6..33dc447890 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -268,7 +268,8 @@ Object.assign(frappe.utils, {

    '); return content.html(); }, - scroll_to: function(element, animate=true, additional_offset, element_to_be_scrolled, callback) { + scroll_to: function(element, animate=true, additional_offset, + element_to_be_scrolled, callback, highlight_element=false) { if (frappe.flags.disable_auto_scroll) return; element_to_be_scrolled = element_to_be_scrolled || $("html, body"); @@ -291,11 +292,20 @@ Object.assign(frappe.utils, { } if (animate) { - element_to_be_scrolled.animate({ scrollTop: scroll_top }).promise().then(callback); + element_to_be_scrolled.animate({ + scrollTop: scroll_top + }, null, () => { + if (highlight_element) { + $(element).addClass('highlight'); + document.addEventListener("click", function() { + $(element).removeClass('highlight'); + }, {once: true}); + } + callback && callback(); + }); } else { element_to_be_scrolled.scrollTop(scroll_top); } - }, get_scroll_position: function(element, additional_offset) { let header_offset = $(".navbar").height() + $(".page-head:visible").height(); diff --git a/frappe/public/scss/common/css_variables.scss b/frappe/public/scss/common/css_variables.scss index 112238bfe5..ede3dd8ba9 100644 --- a/frappe/public/scss/common/css_variables.scss +++ b/frappe/public/scss/common/css_variables.scss @@ -209,6 +209,8 @@ --highlight-color: var(--gray-50); --yellow-highlight-color: var(--yellow-50); + --highlight-shadow: 1px 1px 10px var(--blue-50), 0px 0px 4px var(--blue-600); + // Border Sizes --border-radius-sm: 4px; --border-radius: 6px; diff --git a/frappe/public/scss/desk/dark.scss b/frappe/public/scss/desk/dark.scss index 7f0dfe73b8..35cdffc91a 100644 --- a/frappe/public/scss/desk/dark.scss +++ b/frappe/public/scss/desk/dark.scss @@ -75,6 +75,8 @@ --highlight-color: var(--gray-700); --yellow-highlight-color: var(--yellow-700); + --highlight-shadow: 1px 1px 10px var(--blue-900), 0px 0px 4px var(--blue-500); + // input --input-disabled-bg: none; diff --git a/frappe/public/scss/desk/global.scss b/frappe/public/scss/desk/global.scss index 8c646395e9..d157a43bc3 100644 --- a/frappe/public/scss/desk/global.scss +++ b/frappe/public/scss/desk/global.scss @@ -561,6 +561,19 @@ details > summary:focus { display: none; } +.highlight { + transition: 0.5s ease background-color; + box-shadow: var(--highlight-shadow) !important; +} + +.dropdown-menu.small { + font-size: var(--text-sm); + min-width: 140px; + .dropdown-item { + padding: var(--padding-xs); + } +} + // REDESIGN TODO: Handling of broken images? // img.no-image:before { // .img-background(); diff --git a/frappe/public/scss/desk/timeline.scss b/frappe/public/scss/desk/timeline.scss index a7e5d3dd9c..1861ee018b 100644 --- a/frappe/public/scss/desk/timeline.scss +++ b/frappe/public/scss/desk/timeline.scss @@ -117,7 +117,7 @@ $threshold: 34; .actions { display: flex; - > * { + > *:not(.indicator-pill) { color: var(--text-muted); } }