feat: Shareable link for comments and communications
- Also, highlight comment/communication that has been shared.
This commit is contained in:
parent
3537316a2a
commit
f1c5fc4fe6
9 changed files with 84 additions and 19 deletions
|
|
@ -97,9 +97,13 @@ class BaseTimeline {
|
|||
}
|
||||
|
||||
timeline_item.append(`<div class="timeline-content ${item.is_card ? 'frappe-card' : ''}">`);
|
||||
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(`<span> - ${comment_when(item.creation)}</span>`);
|
||||
timeline_content.append(`<span> - ${comment_when(item.creation)}</span>`);
|
||||
}
|
||||
if (item.id) {
|
||||
timeline_content.attr("id", item.id);
|
||||
}
|
||||
return timeline_item;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = $(`<a class="action-btn reply">${frappe.utils.icon('reply', 'md')}</a>`).click(() => {
|
||||
this.compose_mail(communication_doc);
|
||||
});
|
||||
|
|
@ -446,14 +449,16 @@ class FormTimeline extends BaseTimeline {
|
|||
let edit_wrapper = $(`<div class="comment-edit-box">`).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 = $(`
|
||||
<button class="btn btn-link action-btn">
|
||||
${frappe.utils.icon('close', 'sm')}
|
||||
</button>
|
||||
const delete_option = $(`
|
||||
<li>
|
||||
<a class="dropdown-item">
|
||||
${__("Delete")}
|
||||
</a>
|
||||
</li>
|
||||
`).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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,6 +63,20 @@
|
|||
</svg>
|
||||
</a>
|
||||
{% } %}
|
||||
<div class="custom-actions"></div>
|
||||
<div class="more-actions">
|
||||
<a type="button" class="action-btn"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<svg class="icon icon-sm">
|
||||
<use xlink:href="#icon-dot-horizontal"></use>
|
||||
</svg>
|
||||
</a>
|
||||
<ul class="dropdown-menu small">
|
||||
<li>
|
||||
<a class="dropdown-item" data-action="copy_link">{{ __('Copy Link') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
<div class="content">
|
||||
|
|
|
|||
|
|
@ -268,7 +268,8 @@ Object.assign(frappe.utils, {
|
|||
</a></p>');
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ $threshold: 34;
|
|||
|
||||
.actions {
|
||||
display: flex;
|
||||
> * {
|
||||
> *:not(.indicator-pill) {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue