diff --git a/assets/javascripts/templates/notice_tmpl.js b/assets/javascripts/templates/notice_tmpl.js
index 49793eb571..26c8c947b1 100644
--- a/assets/javascripts/templates/notice_tmpl.js
+++ b/assets/javascripts/templates/notice_tmpl.js
@@ -7,3 +7,9 @@ app.templates.singleDocNotice = (doc) =>
app.templates.disabledDocNotice = () =>
notice(` This documentation is disabled.
To enable it, go to Preferences. `);
+
+app.templates.noOriginalLinkNotice = () =>
+ notice(` The original page link is not available for this documentation. `);
+
+app.templates.copyFailedNotice = () =>
+ notice(` Couldn't copy the original page link to the clipboard. `);
diff --git a/assets/javascripts/views/content/entry_page.js b/assets/javascripts/views/content/entry_page.js
index 32935b4172..2fc24d4b5d 100644
--- a/assets/javascripts/views/content/entry_page.js
+++ b/assets/javascripts/views/content/entry_page.js
@@ -23,6 +23,7 @@ app.views.EntryPage = class EntryPage extends app.View {
deactivate() {
if (super.deactivate(...arguments)) {
+ this.hideTransientNotice();
this.empty();
this.entry = null;
}
@@ -218,20 +219,58 @@ app.views.EntryPage = class EntryPage extends app.View {
}
}
+ originalLink() {
+ // The attribution is appended last but may be followed by other elements,
+ // so match on the last attribution rather than on its sibling position.
+ const links = this.findAll("._attribution ._attribution-link");
+ return links[links.length - 1];
+ }
+
onAltC() {
- const link = this.find("._attribution:last-child ._attribution-link");
+ const link = this.originalLink();
if (!link) {
+ this.showTransientNotice("noOriginalLink");
+ return;
+ }
+ if (!navigator.clipboard) {
+ this.showTransientNotice("copyFailed");
return;
}
- console.log(link.href + location.hash);
- navigator.clipboard.writeText(link.href + location.hash);
+ navigator.clipboard.writeText(link.href + location.hash).catch(() => {
+ // The rejection may arrive after the user navigated away. This view is
+ // reused across entries, so only report the failure while the page that
+ // was copied from is still the one on screen.
+ if (this.activated && link.isConnected) {
+ this.showTransientNotice("copyFailed");
+ }
+ });
}
onAltO() {
- const link = this.find("._attribution:last-child ._attribution-link");
+ const link = this.originalLink();
if (!link) {
+ this.showTransientNotice("noOriginalLink");
return;
}
this.delay(() => $.popup(link.href + location.hash));
}
+
+ showTransientNotice(type) {
+ this.hideTransientNotice();
+ this.transientNotice = new app.views.Notice(type);
+ // Persistent notices (single doc, disabled doc) share the same bounds and
+ // z-index, so raise this one to keep it visible while it's shown.
+ this.transientNotice.addClass("_notice-transient");
+ this.transientNoticeTimer = this.delay(this.hideTransientNotice, 3000);
+ }
+
+ hideTransientNotice() {
+ if (!this.transientNotice) {
+ return;
+ }
+ clearTimeout(this.transientNoticeTimer);
+ this.transientNotice.deactivate();
+ this.transientNotice = null;
+ this.transientNoticeTimer = null;
+ }
};
diff --git a/assets/stylesheets/components/_notice.scss b/assets/stylesheets/components/_notice.scss
index 497a8989b4..9ccbb039a2 100644
--- a/assets/stylesheets/components/_notice.scss
+++ b/assets/stylesheets/components/_notice.scss
@@ -18,6 +18,8 @@
~ ._container { padding-bottom: 2.5rem; }
}
+._notice-transient { z-index: calc(var(--noticeZ) + 1); }
+
._notice-text {
display: table-cell;
vertical-align: middle;