1
0
Fork 0

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

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
}; };
}; };

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 ) {