summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--extensions/BugModal/template/en/default/bug_modal/header.html.tmpl8
-rw-r--r--extensions/BugModal/web/bug_modal.css37
-rw-r--r--extensions/BugModal/web/bug_modal.js87
3 files changed, 126 insertions, 6 deletions
diff --git a/extensions/BugModal/template/en/default/bug_modal/header.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/header.html.tmpl
index a21e9c268..c5ae78af3 100644
--- a/extensions/BugModal/template/en/default/bug_modal/header.html.tmpl
+++ b/extensions/BugModal/template/en/default/bug_modal/header.html.tmpl
@@ -83,12 +83,7 @@
[%# update last-visited %]
[% IF user.id && user.is_involved_in_bug(bug) %]
- $(function() {
- bugzilla_ajax({
- url: 'rest/bug_user_last_visit/[% bug.id FILTER none %]',
- type: 'POST'
- });
- });
+ document.addEventListener('DOMContentLoaded', () => show_new_changes_indicator(), { once: true });
[% END %]
[%# expose useful data to js %]
@@ -101,6 +96,7 @@
is_insider: [% user.is_insider ? "true" : "false" %],
is_timetracker: [% user.is_timetracker ? "true" : "false" %],
can_tag: [% user.can_tag_comments ? "true" : "false" %],
+ timezone: '[% user.timezone.name FILTER js %]',
settings: {
quote_replies: '[% user.settings.quote_replies.value FILTER js %]',
zoom_textareas: [% user.settings.zoom_textareas.value == "on" ? "true" : "false" %],
diff --git a/extensions/BugModal/web/bug_modal.css b/extensions/BugModal/web/bug_modal.css
index eeba78d74..9a3978b14 100644
--- a/extensions/BugModal/web/bug_modal.css
+++ b/extensions/BugModal/web/bug_modal.css
@@ -532,6 +532,43 @@ td.flag-requestee {
text-align: right;
}
+.new-changes-link {
+ margin: 8px 0;
+ border-radius: 4px;
+ padding: 4px;
+ font-size: 12px;
+ text-align: center;
+ color: #FFF;
+ background: #277AC1;
+ cursor: pointer;
+}
+
+.new-changes-separator {
+ position: relative;
+ margin: 16px -8px;
+ height: 0;
+ border-top: 1px solid #C00;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+}
+
+.new-changes-separator span {
+ display: inline-block;
+ position: absolute;
+ top: -10px;
+ right: 16px;
+ border: 1px solid #CCC;
+ border-radius: 4px;
+ padding: 0 4px;
+ height: 16px;
+ font-size: 10px;
+ line-height: 16px;
+ text-transform: uppercase;
+ color: #C00;
+ background-color: #FFF;
+}
+
.change-set {
clear: both;
-webkit-box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1);
diff --git a/extensions/BugModal/web/bug_modal.js b/extensions/BugModal/web/bug_modal.js
index 300ebd3ae..ef015d3f9 100644
--- a/extensions/BugModal/web/bug_modal.js
+++ b/extensions/BugModal/web/bug_modal.js
@@ -1346,6 +1346,93 @@ function confirmUnsafeURL(url) {
'The full URL is:\n\n' + url + '\n\nContinue?');
}
+function show_new_changes_indicator() {
+ const url = `rest/bug_user_last_visit/${BUGZILLA.bug_id}`;
+
+ // Get the last visited timestamp
+ bugzilla_ajax({ url }, data => {
+ // Save the current timestamp
+ bugzilla_ajax({ url, type: 'POST' });
+
+ if (!data[0] || !data[0].last_visit_ts) {
+ return;
+ }
+
+ const last_visit_ts = new Date(data[0].last_visit_ts);
+ const new_changes = [...document.querySelectorAll('main .change-set')].filter($change => {
+ // Exclude hidden CC changes
+ return $change.clientHeight > 0 &&
+ new Date($change.querySelector('[data-time]').getAttribute('data-time') * 1000) > last_visit_ts;
+ });
+
+ if (new_changes.length === 0) {
+ return;
+ }
+
+ const now = new Date();
+ const date_locale = document.querySelector('html').lang;
+ const date_options = {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: 'numeric',
+ hour12: false,
+ timeZone: BUGZILLA.user.timezone,
+ timeZoneName: 'short',
+ };
+
+ if (last_visit_ts.getFullYear() === now.getFullYear()) {
+ delete date_options.year;
+
+ if (last_visit_ts.getMonth() === now.getMonth() && last_visit_ts.getDate() === now.getDate()) {
+ delete date_options.month;
+ delete date_options.day;
+ }
+ }
+
+ const $link = document.createElement('div');
+ const $separator = document.createElement('div');
+ const comments_count = new_changes.filter($change => !!$change.querySelector('.comment')).length;
+ const changes_count = new_changes.length - comments_count;
+ const date_attr = last_visit_ts.toISOString();
+ const date_label = last_visit_ts.toLocaleString(date_locale, date_options);
+
+ // Insert a link
+ $link.className = 'new-changes-link';
+ $link.innerHTML =
+ (c => c === 0 ? '' : (c === 1 ? `${c} new comment` : `${c} new comments`))(comments_count) +
+ (comments_count > 0 && changes_count > 0 ? ', ' : '') +
+ (c => c === 0 ? '' : (c === 1 ? `${c} new change` : `${c} new changes`))(changes_count) +
+ ` since <time datetime="${date_attr}">${date_label}</time>`;
+ $link.addEventListener('click', () => {
+ $link.remove();
+ scroll_element_into_view($separator);
+ }, { once: true });
+ document.querySelector('#changeform').insertAdjacentElement('beforebegin', $link);
+
+ // Insert a separator
+ $separator.className = 'new-changes-separator';
+ $separator.innerHTML = '<span>New</span>';
+ new_changes[0].insertAdjacentElement('beforebegin', $separator);
+
+ // Remove the link once the separator goes into the viewport
+ if ('IntersectionObserver' in window) {
+ const observer = new IntersectionObserver(entries => entries.forEach(entry => {
+ if (entry.intersectionRatio > 0) {
+ observer.unobserve($separator);
+ $link.remove();
+ }
+ }), { root: $separator.offsetParent });
+
+ observer.observe($separator);
+ }
+
+ // TODO: Enable auto-scroll once the modal page layout is optimized
+ // scroll_element_into_view($separator);
+ });
+}
+
// fix url after bug creation/update
if (history && history.replaceState) {
let bug_id = BUGZILLA.bug_id;