diff --git a/js/pacosako_ui.js b/js/pacosako_ui.js index 29c4954..33d91ed 100644 --- a/js/pacosako_ui.js +++ b/js/pacosako_ui.js @@ -169,7 +169,7 @@ $(function (){ animate = false; } - $('#cb_board').data('last_state', currentGame.moves); + notifyLocalMove(currentGame, $('#cb_board').data('gameId')); setCurrentGame(currentGame, animate); putState(); } @@ -404,28 +404,8 @@ $(function (){ } function setCurrentGame(game, animate) { - closeNotifications(); - currentGame = game; setVisibleGame(game, animate); - - const moves = game.moves; - - const cb_board = $('#cb_board').first(); - if (!deepEqual(moves, cb_board.data('last_state'))) { - /* ignore partial moves */ - if (!game.board.phantom) { - if ($('#cb_notify')[0].checked) { - const gameString = cb_board.data('lightName') + ' vs. ' + cb_board.data('darkName'); - notify(gameString + '\n' + $('#cb_message').text()); - } - if ($('#cb_sound')[0].checked) { - playNotifySound(); - } - } - } - - cb_board.data('last_state', moves); } function randomId(){ @@ -588,7 +568,7 @@ $(function (){ function putState() { const boardElem = $('#cb_board'); const gameId = boardElem.data('gameId'); - boardElem.data('last_state', currentGame.moves); + notifyLocalMove(currentGame, boardElem.data('gameId')); putMeta({ board: JSON.parse(currentGame.toJSON()) }); } @@ -640,18 +620,9 @@ $(function (){ boardElem.data('modified', 0); history.replaceState(null, document.title, '#/' + newId); - const notifyAfter = +new Date() + 2000; - $(window).data('notifyAfter', +new Date() + 2000); - window.setTimeout(function() { - /* Delete the notification block in case the system time is changed backward */ - if ($(window).data('notifyAfter') === notifyAfter) { - $(window).removeData('notifyAfter'); - } - }, 2000); - /* this will be the starting state if no data is received from peers */ setCurrentGame(new PS.Game()); - boardElem.data('last_state', currentGame.moves); + notifyLocalMove(null, newId); boardElem.data('lightName', 'Light'); boardElem.data('darkName', 'Dark'); @@ -733,49 +704,39 @@ $(function (){ const notifyAudio = new Audio(Waterdrop); function playNotifySound(){ - const now = +new Date(); - const then = $(window).data('notifyAfter'); - if (!then || now >= then) { - try { notifyAudio.play(); } catch (err) {} - } + try { notifyAudio.play(); } catch (err) {} } function notify(body) { - const now = +new Date(); - const then = $(window).data('notifyAfter'); - if (!then || now >= then) { - try { - Notification.requestPermission(function(permission){ - if (permission === 'granted') { - navigator.serviceWorker.ready.then(function(registration){ - registration.showNotification('Paco Ŝako', { - body: body, - tag: 'notice', - }); + try { + Notification.requestPermission(function(permission){ + if (permission === 'granted') { + navigator.serviceWorker.ready.then(function(registration){ + registration.showNotification('Paco Ŝako', { + body: body, + tag: 'notice', }); - } else if (permission === 'denied') { - disableNotify(); - } - }); - } catch (err) { - disableNotify(); - } + }); + } else if (permission === 'denied') { + disableNotify(); + } + }); + } catch (err) { + disableNotify(); } } - function closeNotifications(){ + async function closeNotifications() { try { - navigator.serviceWorker.ready.then(function(registration){ - try { - if ('getNotifications' in registration) { - registration.getNotifications({tag: 'notice'}).then(function(notifications){ - for (const notification of notifications) { - notification.close(); - } - }); - } - } catch (err) {} - }); + const registration = await navigator.serviceWorker.ready; + + if ('getNotifications' in registration) { + const notifications = await registration.getNotifications({tag: 'notice'}); + + for (const notification of notifications) { + notification.close(); + } + } } catch (err) {} } @@ -998,7 +959,7 @@ $(function (){ $('#cb_undo').on('click', function(){ if (currentGame.canUndo) { currentGame.undo(); - $('#cb_board').data('last_state', currentGame.moves); + notifyLocalMove(currentGame, $('#cb_board').data('gameId')); setCurrentGame(currentGame); putState(); } @@ -1007,7 +968,7 @@ $(function (){ $('#cb_redo').on('click', function(){ if (currentGame.canRedo) { currentGame.redo(); - $('#cb_board').data('last_state', currentGame.moves); + notifyLocalMove(currentGame, $('#cb_board').data('gameId')); setCurrentGame(currentGame, true); putState(); } @@ -1021,7 +982,7 @@ $(function (){ debug('unable to resign', err); } - $('#cb_board').data('last_state', currentGame.moves); + notifyLocalMove(currentGame, $('#cb_board').data('gameId')); setCurrentGame(currentGame); putState(); }); @@ -1259,6 +1220,70 @@ $(function (){ IO.onMetaUpdate(updateSelectGameMeta); + const lastNotifyState = {}; + + function notifyForGame(meta, gameId) { + const notifyList = $('#cb_notify').data('gameList') || []; + if (!notifyList.includes('*') && !notifyList.includes(gameId)) { + return; + } + + lastNotifyState[gameId] = lastNotifyState[gameId] || {}; + const lastState = lastNotifyState[gameId]; + const lastMetaState = lastState.meta || {}; + const changed = + meta.status !== lastMetaState.status || + meta.moves !== lastMetaState.moves || + meta.timestamp !== lastMetaState.timestamp; + + if (lastState.meta && changed) { + IO.getGameState(gameId).then((data) => { + let game = undefined; + try { + game = new PS.Game(JSON.stringify(data.board)); + } catch (err) { + debug('failed to parse game for notification', err); + return; + } + + const moves = game.moves; + + if (!deepEqual(moves, lastState.moves)) { + if (!game.board.phantom && moves.length > (lastState.moves || []).length) { + const lightName = shortenName(String(data.lightName || 'Light')); + const darkName = shortenName(String(data.darkName || 'Dark')); + const message = gameMessage(game); + const gameString = `${lightName} vs. ${darkName}\n${message}`; + closeNotifications().then(() => { + notify(gameString); + }); + + if ($('#cb_sound').prop('checked')) { + playNotifySound(); + } + } + + lastState.moves = moves; + } + }).catch((err) => { + debug('failed to retrieve game data for notification', err); + }); + } + + lastState.meta = meta; + } + + function notifyLocalMove(game, gameId) { + if (!game) { + delete lastNotifyState[gameId]; + } else { + lastNotifyState[gameId] = lastNotifyState[gameId] || {}; + lastNotifyState[gameId].moves = game.moves; + } + } + + IO.onMetaUpdate(notifyForGame); + window.onpopstate = function(event){ const foundId = location.hash.match(/^#\/([0-9a-f]{16}\b)/); if (foundId) {