fiv out of sync speaker view after presentation reloads #2822 #3032

This commit is contained in:
hakimel
2022-02-10 13:28:37 +01:00
parent 6b535328c0
commit ff20051861
4 changed files with 174 additions and 121 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+75 -35
View File
@@ -15,24 +15,48 @@ import marked from 'marked';
*/ */
const Plugin = () => { const Plugin = () => {
let popup = null; let connectInterval;
let speakerWindow = null;
let deck; let deck;
function openNotes() { /**
* Opens a new speaker view window.
*/
function openSpeakerWindow() {
if (popup && !popup.closed) { // If a window is already open, focus it
popup.focus(); if( speakerWindow && !speakerWindow.closed ) {
speakerWindow.focus();
}
else {
speakerWindow = window.open( 'about:blank', 'reveal.js - Notes', 'width=1100,height=700' );
speakerWindow.marked = marked;
speakerWindow.document.write( speakerViewHTML );
if( !speakerWindow ) {
alert( 'Speaker view popup failed to open. Please make sure popups are allowed and reopen the speaker view.' );
return; return;
} }
popup = window.open( 'about:blank', 'reveal.js - Notes', 'width=1100,height=700' ); connect();
popup.marked = marked; }
popup.document.write( speakerViewHTML );
}
/**
* Reconnect with an existing speaker view window.
*/
function reconnectSpeakerWindow( reconnectWindow ) {
if( speakerWindow && !speakerWindow.closed ) {
speakerWindow.focus();
}
else {
speakerWindow = reconnectWindow;
window.addEventListener( 'message', onPostMessage );
onConnected();
}
if( !popup ) {
alert( 'Speaker view popup failed to open. Please make sure popups are allowed and reopen the speaker view.' );
return;
} }
/** /**
@@ -42,9 +66,10 @@ const Plugin = () => {
* file system. * file system.
*/ */
function connect() { function connect() {
// Keep trying to connect until we get a 'connected' message back // Keep trying to connect until we get a 'connected' message back
let connectInterval = setInterval( function() { connectInterval = setInterval( function() {
popup.postMessage( JSON.stringify( { speakerWindow.postMessage( JSON.stringify( {
namespace: 'reveal-notes', namespace: 'reveal-notes',
type: 'connect', type: 'connect',
url: window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search, url: window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search,
@@ -52,16 +77,8 @@ const Plugin = () => {
} ), '*' ); } ), '*' );
}, 500 ); }, 500 );
window.addEventListener( 'message', function( event ) { window.addEventListener( 'message', onPostMessage );
let data = JSON.parse( event.data );
if( data && data.namespace === 'reveal-notes' && data.type === 'connected' ) {
clearInterval( connectInterval );
onConnected();
}
if( data && data.namespace === 'reveal-notes' && data.type === 'call' ) {
callRevealApi( data.methodName, data.arguments, data.callId );
}
} );
} }
/** /**
@@ -71,17 +88,17 @@ const Plugin = () => {
function callRevealApi( methodName, methodArguments, callId ) { function callRevealApi( methodName, methodArguments, callId ) {
let result = deck[methodName].apply( deck, methodArguments ); let result = deck[methodName].apply( deck, methodArguments );
popup.postMessage( JSON.stringify( { speakerWindow.postMessage( JSON.stringify( {
namespace: 'reveal-notes', namespace: 'reveal-notes',
type: 'return', type: 'return',
result: result, result,
callId: callId callId
} ), '*' ); } ), '*' );
} }
/** /**
* Posts the current slide data to the notes window * Posts the current slide data to the notes window.
*/ */
function post( event ) { function post( event ) {
@@ -125,7 +142,20 @@ const Plugin = () => {
messageData.markdown = typeof notesElement.getAttribute( 'data-markdown' ) === 'string'; messageData.markdown = typeof notesElement.getAttribute( 'data-markdown' ) === 'string';
} }
popup.postMessage( JSON.stringify( messageData ), '*' ); speakerWindow.postMessage( JSON.stringify( messageData ), '*' );
}
function onPostMessage( event ) {
let data = JSON.parse( event.data );
if( data && data.namespace === 'reveal-notes' && data.type === 'connected' ) {
clearInterval( connectInterval );
onConnected();
}
else if( data && data.namespace === 'reveal-notes' && data.type === 'call' ) {
callRevealApi( data.methodName, data.arguments, data.callId );
}
} }
@@ -149,10 +179,6 @@ const Plugin = () => {
} }
connect();
}
return { return {
id: 'notes', id: 'notes',
@@ -164,19 +190,33 @@ const Plugin = () => {
// If the there's a 'notes' query set, open directly // If the there's a 'notes' query set, open directly
if( window.location.search.match( /(\?|\&)notes/gi ) !== null ) { if( window.location.search.match( /(\?|\&)notes/gi ) !== null ) {
openNotes(); openSpeakerWindow();
}
else {
// Keep listening for speaker view hearbeats. If we receive a
// heartbeat from an orphaned window, reconnect it. This ensures
// that we remain connected to the notes even if the presentation
// is reloaded.
window.addEventListener( 'message', event => {
if( !speakerWindow ) {
let data = JSON.parse( event.data );
if( data && data.namespace === 'reveal-notes' && data.type === 'heartbeat' ) {
reconnectSpeakerWindow( event.source );
}
}
});
} }
// Open the notes when the 's' key is hit // Open the notes when the 's' key is hit
deck.addKeyBinding({keyCode: 83, key: 'S', description: 'Speaker notes view'}, function() { deck.addKeyBinding({keyCode: 83, key: 'S', description: 'Speaker notes view'}, function() {
openNotes(); openSpeakerWindow();
} ); } );
} }
}, },
open: openNotes open: openSpeakerWindow
}; };
}; };
+13
View File
@@ -435,6 +435,7 @@
setupKeyboard(); setupKeyboard();
setupNotes(); setupNotes();
setupTimer(); setupTimer();
setupHeartbeat();
} }
} }
@@ -536,6 +537,18 @@
} }
/**
* We send out a heartbeat at all times to ensure we can
* reconnect with the main presentation window after reloads.
*/
function setupHeartbeat() {
setInterval( () => {
window.opener.postMessage( JSON.stringify({ namespace: 'reveal-notes', type: 'heartbeat'} ), '*' );
}, 1000 );
}
function getTimings( callback ) { function getTimings( callback ) {
callRevealApi( 'getSlidesAttributes', [], function ( slideAttributes ) { callRevealApi( 'getSlidesAttributes', [], function ( slideAttributes ) {