@ -1,30 +1,30 @@
setDefaultLocalStorage ( 'live' , true ) ;
setDefaultLocalStorage ( 'notifications' , false ) ;
setDefaultLocalStorage ( 'scroll' , false ) ;
var socket ;
let liveEnabled = localStorage . getItem ( 'live' ) == 'true' ;
let notificationsEnabled = localStorage . getItem ( 'notifications' ) == 'true' ;
let scrollEnabled = localStorage . getItem ( 'scroll' ) == 'true' ;
let socket ;
let forceUpdate ;
window . addEventListener ( 'settingsReady' , function ( event ) { //after domcontentloaded
let supportsWebSockets = 'WebSocket' in window || 'MozWebSocket' in window ;
const livecolor = document . getElementById ( 'livecolor' ) ;
const livetext = isThread ? document . getElementById ( 'livetext' ) . childNodes [ 1 ] : null ;
const updateButton = livetext ? livetext . nextSibling : null ;
const updateLive = ( message , color ) => {
const updateLive = ( message , color , showRelativeTime ) => {
livecolor . style . backgroundColor = color ;
livetext . nodeValue = message ;
livetext . nodeValue = ` ${ message } ` ;
}
let lastPostId ;
let liveTimeout ;
const anchors = document . getElementsByClassName ( 'anchor' ) ;
let liveEnabled = localStorage . getItem ( 'live' ) == 'true' ;
let notificationsEnabled = localStorage . getItem ( 'notifications' ) == 'true' ;
let scrollEnabled = localStorage . getItem ( 'scroll' ) == 'true' ;
if ( anchors . length > 0 ) {
lastPostId = anchors [ anchors . length - 1 ] . id ;
}
const thread = document . querySelector ( '.thread' ) ;
const newPost = ( data ) => {
console . log ( 'got new post' ) ;
lastPostId = data . postId ;
@ -83,111 +83,132 @@ window.addEventListener('settingsReady', function(event) { //after domcontentloa
window . dispatchEvent ( newPostEvent ) ;
}
const jsonPath = window . location . pathname . replace ( /\.html$/ , '.json' ) ;
const jsonCatchup = async ( ) => {
console . log ( 'catching up after reconnect ' ) ;
const fetchNewPosts = async ( ) => {
console . log ( 'fetching posts from api ' ) ;
updateLive ( 'Fetching posts...' , 'yellow' ) ;
let json ;
let newPosts = [ ] ;
try {
json = await fetch ( jsonPath ) . then ( res => res . json ( ) ) ;
} catch ( e ) {
console . error ( e ) ;
}
if ( json && json . replies && json . replies . length > 0 ) {
const newPosts = json . replies . filter ( r => r . postId > lastPostId ) ; //filter to only newer posts
newPosts = json . replies . filter ( r => r . postId > lastPostId ) ; //filter to only newer posts
if ( newPosts . length > 0 ) {
for ( let i = 0 ; i < newPosts . length ; i ++ ) {
newPost ( newPosts [ i ] ) ;
}
}
}
updateLive ( 'Updated' , 'green' ) ;
return newPosts . length ;
}
const startLive = ( ) => {
const roomParts = window . location . pathname . replace ( /\.html$/ , '' ) . split ( '/' ) ;
const room = ` ${ roomParts [ 1 ] } - ${ roomParts [ 3 ] } ` ;
socket = io ( { transports : [ 'websocket' ] } ) ;
socket . on ( 'connect' , ( ) => {
updateButton . style . display = 'none' ;
console . log ( 'joined room' , room ) ;
updateLive ( 'Connected for live posts' , '#0de600' ) ;
socket . emit ( 'room' , room ) ;
} ) ;
socket . on ( 'pong' , ( latency ) => {
updateButton . style . display = 'none' ;
if ( socket . connected ) {
updateLive ( ` Connected for live posts ( ${ latency } ms) ` , '#0de600' ) ;
}
} ) ;
socket . on ( 'reconnect_attempt' , ( ) => {
updateLive ( 'Attempting to reconnect...' , 'yellow' ) ;
} ) ;
socket . on ( 'disconnect' , ( ) => {
updateButton . removeAttribute ( 'style' ) ;
console . log ( 'lost connection to room' ) ;
updateLive ( 'Disconnected' , 'red' ) ;
} ) ;
socket . on ( 'reconnect' , ( ) => {
let interval = 5000 ;
forceUpdate = async ( ) => {
updateButton . disabled = true ;
clearTimeout ( liveTimeout ) ;
if ( ( await fetchNewPosts ( ) ) > 0 ) {
interval = 5000 ;
} else {
interval = Math . min ( interval * 2 , 90000 ) ;
}
setTimeout ( ( ) => {
updateButton . disabled = false ;
} , 10000 ) ;
if ( liveEnabled ) {
liveTimeout = setTimeout ( forceUpdate , interval ) ;
}
}
const enableLive = ( ) => {
if ( supportsWebSockets ) {
updateButton . style . display = 'none' ;
console . log ( 'reconnected to room' ) ;
jsonCatchup ( ) ;
} ) ;
socket . on ( 'error' , ( e ) => {
updateButton . removeAttribute ( 'style' ) ;
updateLive ( 'Socket error' , 'orange' ) ;
console . error ( e ) ;
} ) ;
socket . on ( 'connect_error' , ( e ) => {
updateButton . removeAttribute ( 'style' ) ;
updateLive ( 'Error connecting' , 'orange' ) ;
console . error ( e ) ;
} ) ;
socket . on ( 'reconnect_error' , ( e ) => {
const roomParts = window . location . pathname . replace ( /\.html$/ , '' ) . split ( '/' ) ;
const room = ` ${ roomParts [ 1 ] } - ${ roomParts [ 3 ] } ` ;
socket = io ( {
transports : [ 'websocket' ] ,
reconnectionAttempts : 5
} ) ;
socket . on ( 'connect' , async ( ) => {
console . log ( 'socket connected' ) ;
await fetchNewPosts ( ) ;
socket . emit ( 'room' , room ) ;
} ) ;
socket . on ( 'message' , ( message ) => {
console . log ( message , room ) ;
if ( message === 'joined' ) {
updateLive ( 'Connected for live posts' , '#0de600' ) ;
}
} ) ;
socket . on ( 'pong' , ( latency ) => {
if ( socket . connected ) {
updateLive ( ` Connected for live posts ( ${ latency } ms) ` , '#0de600' ) ;
}
} ) ;
socket . on ( 'reconnect_attempt' , ( ) => {
updateLive ( 'Attempting to reconnect...' , 'yellow' ) ;
} ) ;
socket . on ( 'disconnect' , ( ) => {
console . log ( 'lost connection to room' ) ;
updateLive ( 'Disconnected' , 'red' ) ;
} ) ;
socket . on ( 'reconnect' , ( ) => {
console . log ( 'reconnected to room' ) ;
fetchNewPosts ( ) ;
} ) ;
socket . on ( 'error' , ( e ) => {
updateLive ( 'Socket error' , 'orange' ) ;
console . error ( e ) ;
} ) ;
socket . on ( 'connect_error' , ( e ) => {
updateLive ( 'Error connecting' , 'orange' ) ;
console . error ( e ) ;
} ) ;
socket . on ( 'reconnect_error' , ( e ) => {
updateLive ( 'Error reconnecting' , 'orange' ) ;
console . error ( e ) ;
} ) ;
socket . on ( 'reconnect_failed' , ( e ) => {
updateLive ( 'Failed reconnecting' , 'orange' ) ;
console . error ( e ) ;
console . log ( 'failed to reconnnect, falling back to polling' )
socket . close ( ) ;
supportsWebSockets = false ;
enableLive ( ) ;
} ) ;
socket . on ( 'newPost' , newPost ) ;
} else {
//websocket not supported, update with polling to api
updateButton . removeAttribute ( 'style' ) ;
updateLive ( 'Error reconnecting' , 'orange' ) ;
console . error ( e ) ;
} ) ;
socket . on ( 'newPost' , newPost ) ;
}
forceUpdate ( ) ;
}
} ;
const liveSetting = document . getElementById ( 'live-setting' ) ;
const notificationSetting = document . getElementById ( 'notification-setting' ) ;
const scrollSetting = document . getElementById ( 'scroll-setting' ) ;
const disableLive = ( ) => {
updateButton . removeAttribute ( 'style' ) ;
clearTimeout ( liveTimeout ) ;
if ( socket && supportsWebSockets ) {
socket . disconnect ( ) ;
}
updateLive ( 'Live posts off' , 'darkgray' ) ;
} ;
const liveSetting = document . getElementById ( 'live-setting' ) ;
const toggleLive = ( ) => {
if ( isThread ) {
if ( socket && liveEnabled ) {
socket . disconnect ( ) ;
updateLive ( 'Live posts disabled' , 'red' ) ;
} else {
if ( ! socket ) {
startLive ( ) ;
} else {
socket . connect ( ) ;
}
jsonCatchup ( ) ;
}
}
liveEnabled = ! liveEnabled ;
if ( ! liveEnabled ) {
//disable notifications and scroll when live off because they wont work
if ( notificationsEnabled ) {
notificationSetting . checked = false ;
toggleNotifications ( null , true ) ;
}
if ( scrollEnabled ) {
scrollSetting . checked = false ;
toggleScroll ( null , true ) ;
}
}
liveEnabled ? enableLive ( ) : disableLive ( ) ;
console . log ( 'toggling live posts' , liveEnabled ) ;
setLocalStorage ( 'live' , liveEnabled ) ;
}
liveSetting . checked = liveEnabled ;
liveSetting . addEventListener ( 'change' , toggleLive , false ) ;
const toggleNotifications = async ( change , changeFromConflict ) => {
const notificationSetting = document . getElementById ( 'notification-setting' ) ;
const toggleNotifications = async ( ) => {
notificationsEnabled = ! notificationsEnabled ;
if ( notificationsEnabled ) {
const result = await Notification . requestPermission ( )
@ -200,42 +221,22 @@ window.addEventListener('settingsReady', function(event) { //after domcontentloa
}
console . log ( 'toggling notifications' , notificationsEnabled ) ;
setLocalStorage ( 'notifications' , notificationsEnabled ) ;
if ( ! liveEnabled && ! changeFromConflict ) {
liveSetting . checked = true ;
toggleLive ( ) ;
}
}
notificationSetting . checked = notificationsEnabled ;
notificationSetting . addEventListener ( 'change' , toggleNotifications , false ) ;
const toggleScroll = ( change , changeFromConflict ) => {
const scrollSetting = document . getElementById ( 'scroll-setting' ) ;
const toggleScroll = ( ) => {
scrollEnabled = ! scrollEnabled ;
console . log ( 'toggling post scrolling' , scrollEnabled ) ;
setLocalStorage ( 'scroll' , scrollEnabled ) ;
if ( ! liveEnabled && ! changeFromConflict ) {
liveSetting . checked = true ;
toggleLive ( ) ;
}
}
scrollSetting . checked = scrollEnabled ;
scrollSetting . addEventListener ( 'change' , toggleScroll , false ) ;
if ( isThread ) {
const forceUpdate = async ( ) => {
updateButton . disabled = true ;
await jsonCatchup ( ) ;
updateLive ( 'Updated' , 'green' ) ;
setTimeout ( ( ) => {
updateButton . disabled = false ;
} , 10000 ) ;
}
updateButton . addEventListener ( 'click' , forceUpdate ) ;
if ( liveEnabled ) {
updateButton . style . display = 'none' ;
startLive ( ) ;
} else {
updateLive ( 'Live posts disabled' , 'red' ) ;
}
liveEnabled ? enableLive ( ) : disableLive ( ) ;
}
} ) ;