@ -729,6 +729,9 @@ function appendReplyForm(el) {
requestAnimationFrame ( ( ) => writeInput . focus ( ) ) ;
requestAnimationFrame ( ( ) => writeInput . focus ( ) ) ;
}
}
const lockScroll = ( ) => document . body . style . overflow = 'hidden' ;
const unlockScroll = ( ) => document . body . style . removeProperty ( 'overflow' ) ;
const newMessageDiv = document . querySelector ( '#newMessage' ) ;
const newMessageDiv = document . querySelector ( '#newMessage' ) ;
document . querySelector ( '#bubble' ) . addEventListener ( 'click' , ( e ) => {
document . querySelector ( '#bubble' ) . addEventListener ( 'click' , ( e ) => {
localStorage . removeItem ( 'reply_to' ) ; // should it forget old replyto context?
localStorage . removeItem ( 'reply_to' ) ; // should it forget old replyto context?
@ -738,7 +741,7 @@ document.querySelector('#bubble').addEventListener('click', (e) => {
if ( writeInput . value . trimRight ( ) ) {
if ( writeInput . value . trimRight ( ) ) {
writeInput . style . removeProperty ( 'height' ) ;
writeInput . style . removeProperty ( 'height' ) ;
}
}
document . body . style . overflow = 'hidden' ;
lockScroll ( ) ;
requestAnimationFrame ( ( ) => updateElemHeight ( writeInput ) ) ;
requestAnimationFrame ( ( ) => updateElemHeight ( writeInput ) ) ;
} ) ;
} ) ;
@ -749,7 +752,7 @@ document.body.addEventListener('keyup', (e) => {
} ) ;
} ) ;
function hideNewMessage ( hide ) {
function hideNewMessage ( hide ) {
document . body . style . removeProperty ( 'overflow' ) ;
unlockScroll ( ) ;
newMessageDiv . hidden = hide ;
newMessageDiv . hidden = hide ;
}
}
@ -768,7 +771,8 @@ async function upvote(eventId, eventPubkey) {
content : '+' ,
content : '+' ,
tags ,
tags ,
created _at : Math . floor ( Date . now ( ) * 0.001 ) ,
created _at : Math . floor ( Date . now ( ) * 0.001 ) ,
} , difficulty ) ;
} , difficulty , 10 ) . catch ( console . warn ) ;
if ( newReaction ) {
const sig = await signEvent ( newReaction , privatekey ) . catch ( console . error ) ;
const sig = await signEvent ( newReaction , privatekey ) . catch ( console . error ) ;
if ( sig ) {
if ( sig ) {
const ev = await pool . publish ( { ... newReaction , sig } , ( status , url ) => {
const ev = await pool . publish ( { ... newReaction , sig } , ( status , url ) => {
@ -780,6 +784,7 @@ async function upvote(eventId, eventPubkey) {
}
}
} ) . catch ( console . error ) ;
} ) . catch ( console . error ) ;
}
}
}
}
}
// send
// send
@ -805,7 +810,8 @@ writeForm.addEventListener('submit', async (e) => {
pubkey ,
pubkey ,
tags ,
tags ,
created _at : Math . floor ( Date . now ( ) * 0.001 ) ,
created _at : Math . floor ( Date . now ( ) * 0.001 ) ,
} , difficulty ) ;
} , difficulty , 10 ) . catch ( console . warn ) ;
if ( newEvent ) {
const sig = await signEvent ( newEvent , privatekey ) . catch ( onSendError ) ;
const sig = await signEvent ( newEvent , privatekey ) . catch ( onSendError ) ;
if ( sig ) {
if ( sig ) {
const ev = await pool . publish ( { ... newEvent , sig } , ( status , url ) => {
const ev = await pool . publish ( { ... newEvent , sig } , ( status , url ) => {
@ -826,6 +832,7 @@ writeForm.addEventListener('submit', async (e) => {
}
}
} ) ;
} ) ;
}
}
}
} ) ;
} ) ;
writeInput . addEventListener ( 'input' , ( ) => {
writeInput . addEventListener ( 'input' , ( ) => {
@ -947,7 +954,8 @@ profileForm.addEventListener('submit', async (e) => {
content : JSON . stringify ( Object . fromEntries ( form ) ) ,
content : JSON . stringify ( Object . fromEntries ( form ) ) ,
tags : [ ] ,
tags : [ ] ,
created _at : Math . floor ( Date . now ( ) * 0.001 ) ,
created _at : Math . floor ( Date . now ( ) * 0.001 ) ,
} , difficulty ) ;
} , difficulty , 10 ) . catch ( console . warn ) ;
if ( newProfile ) {
const sig = await signEvent ( newProfile , privatekey ) . catch ( console . error ) ;
const sig = await signEvent ( newProfile , privatekey ) . catch ( console . error ) ;
if ( sig ) {
if ( sig ) {
const ev = await pool . publish ( { ... newProfile , sig } , ( status , url ) => {
const ev = await pool . publish ( { ... newProfile , sig } , ( status , url ) => {
@ -961,8 +969,42 @@ profileForm.addEventListener('submit', async (e) => {
}
}
} ) . catch ( console . error ) ;
} ) . catch ( console . error ) ;
}
}
}
} ) ;
} ) ;
const errorOverlay = document . querySelector ( '#errorOverlay' ) ;
function promptError ( error , options = { } ) {
const { onAgain , onCancel } = options ;
lockScroll ( ) ;
errorOverlay . replaceChildren (
elem ( 'h1' , { className : 'error-title' } , error ) ,
elem ( 'p' , { } , 'something went wrong' ) ,
elem ( 'div' , { className : 'buttons' } , [
onCancel ? elem ( 'button' , { data : { action : 'close' } } , 'close' ) : '' ,
onAgain ? elem ( 'button' , { data : { action : 'again' } } , 'try again' ) : '' ,
] ) ,
) ;
const handleOverlayClick = ( e ) => {
const button = e . target . closest ( 'button' ) ;
if ( button ) {
switch ( button . dataset . action ) {
case 'close' :
onCancel ( ) ;
break ;
case 'again' :
onAgain ( ) ;
break ;
}
errorOverlay . removeEventListener ( 'click' , handleOverlayClick ) ;
errorOverlay . hidden = true ;
unlockScroll ( ) ;
}
} ;
errorOverlay . addEventListener ( 'click' , handleOverlayClick ) ;
errorOverlay . hidden = false ;
}
/ * *
/ * *
* validate proof - of - work of a nostr event per nip - 13.
* validate proof - of - work of a nostr event per nip - 13.
* the validation always requires difficulty commitment in the nonce tag .
* the validation always requires difficulty commitment in the nonce tag .
@ -990,14 +1032,20 @@ function validatePow(evt) {
* powEvent returns a rejected promise if the funtion runs for longer than timeout .
* powEvent returns a rejected promise if the funtion runs for longer than timeout .
* a zero timeout makes mineEvent run without a time limit .
* a zero timeout makes mineEvent run without a time limit .
* /
* /
function powEvent ( evt , difficulty , timeout ) {
function powEvent ( evt , difficulty , timeout = 0 ) {
return new Promise ( ( resolve , reject ) => {
return new Promise ( ( resolve , reject ) => {
const worker = new Worker ( './worker.js' ) ;
const worker = new Worker ( './worker.js' ) ;
worker . onmessage = ( msg ) => {
worker . onmessage = ( msg ) => {
worker . terminate ( ) ;
worker . terminate ( ) ;
if ( msg . data . error ) {
if ( msg . data . error ) {
reject ( msg . data . error ) ;
promptError ( msg . data . error , {
onCancel : ( ) => reject ( 'canceled' ) ,
onAgain : async ( ) => {
const result = await powEvent ( evt , difficulty , timeout ) . catch ( console . warn ) ;
resolve ( result ) ;
}
} )
} else {
} else {
resolve ( msg . data . event ) ;
resolve ( msg . data . event ) ;
}
}