@@ -23,6 +23,7 @@ app.views.EntryPage = class EntryPage extends app.View {
2323
2424 deactivate ( ) {
2525 if ( super . deactivate ( ...arguments ) ) {
26+ this . hideTransientNotice ( ) ;
2627 this . empty ( ) ;
2728 this . entry = null ;
2829 }
@@ -218,20 +219,58 @@ app.views.EntryPage = class EntryPage extends app.View {
218219 }
219220 }
220221
222+ originalLink ( ) {
223+ // The attribution is appended last but may be followed by other elements,
224+ // so match on the last attribution rather than on its sibling position.
225+ const links = this . findAll ( "._attribution ._attribution-link" ) ;
226+ return links [ links . length - 1 ] ;
227+ }
228+
221229 onAltC ( ) {
222- const link = this . find ( "._attribution:last-child ._attribution-link" ) ;
230+ const link = this . originalLink ( ) ;
223231 if ( ! link ) {
232+ this . showTransientNotice ( "noOriginalLink" ) ;
233+ return ;
234+ }
235+ if ( ! navigator . clipboard ) {
236+ this . showTransientNotice ( "copyFailed" ) ;
224237 return ;
225238 }
226- console . log ( link . href + location . hash ) ;
227- navigator . clipboard . writeText ( link . href + location . hash ) ;
239+ navigator . clipboard . writeText ( link . href + location . hash ) . catch ( ( ) => {
240+ // The rejection may arrive after the user navigated away. This view is
241+ // reused across entries, so only report the failure while the page that
242+ // was copied from is still the one on screen.
243+ if ( this . activated && link . isConnected ) {
244+ this . showTransientNotice ( "copyFailed" ) ;
245+ }
246+ } ) ;
228247 }
229248
230249 onAltO ( ) {
231- const link = this . find ( "._attribution:last-child ._attribution-link" ) ;
250+ const link = this . originalLink ( ) ;
232251 if ( ! link ) {
252+ this . showTransientNotice ( "noOriginalLink" ) ;
233253 return ;
234254 }
235255 this . delay ( ( ) => $ . popup ( link . href + location . hash ) ) ;
236256 }
257+
258+ showTransientNotice ( type ) {
259+ this . hideTransientNotice ( ) ;
260+ this . transientNotice = new app . views . Notice ( type ) ;
261+ // Persistent notices (single doc, disabled doc) share the same bounds and
262+ // z-index, so raise this one to keep it visible while it's shown.
263+ this . transientNotice . addClass ( "_notice-transient" ) ;
264+ this . transientNoticeTimer = this . delay ( this . hideTransientNotice , 3000 ) ;
265+ }
266+
267+ hideTransientNotice ( ) {
268+ if ( ! this . transientNotice ) {
269+ return ;
270+ }
271+ clearTimeout ( this . transientNoticeTimer ) ;
272+ this . transientNotice . deactivate ( ) ;
273+ this . transientNotice = null ;
274+ this . transientNoticeTimer = null ;
275+ }
237276} ;
0 commit comments