@@ -292,13 +292,49 @@ <h1>Nostr</h1>
292292 return new Date ( ts * 1000 ) . toLocaleDateString ( )
293293 }
294294
295+ const BECH32_CHARS = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'
296+ function bech32Encode ( hrp , data5bit ) {
297+ function polymod ( values ) {
298+ const GEN = [ 0x3b6a57b2 , 0x26508e6d , 0x1ea119fa , 0x3d4233dd , 0x2a1462b3 ]
299+ let chk = 1
300+ for ( const v of values ) { const b = chk >> 25 ; chk = ( ( chk & 0x1ffffff ) << 5 ) ^ v ; for ( let i = 0 ; i < 5 ; i ++ ) if ( ( b >> i ) & 1 ) chk ^= GEN [ i ] }
301+ return chk
302+ }
303+ function hrpExpand ( h ) { const r = [ ] ; for ( let i = 0 ; i < h . length ; i ++ ) r . push ( h . charCodeAt ( i ) >> 5 ) ; r . push ( 0 ) ; for ( let i = 0 ; i < h . length ; i ++ ) r . push ( h . charCodeAt ( i ) & 31 ) ; return r }
304+ const values = hrpExpand ( hrp ) . concat ( data5bit )
305+ const pm = polymod ( values . concat ( [ 0 , 0 , 0 , 0 , 0 , 0 ] ) ) ^ 1
306+ const checksum = [ ]
307+ for ( let i = 0 ; i < 6 ; i ++ ) checksum . push ( ( pm >> ( 5 * ( 5 - i ) ) ) & 31 )
308+ return hrp + '1' + data5bit . concat ( checksum ) . map ( d => BECH32_CHARS [ d ] ) . join ( '' )
309+ }
310+ function convertBits ( data , fromBits , toBits , pad ) {
311+ let acc = 0 , bits = 0 ; const ret = [ ] , maxv = ( 1 << toBits ) - 1
312+ for ( const v of data ) { acc = ( acc << fromBits ) | v ; bits += fromBits ; while ( bits >= toBits ) { bits -= toBits ; ret . push ( ( acc >> bits ) & maxv ) } }
313+ if ( pad ) { if ( bits > 0 ) ret . push ( ( acc << ( toBits - bits ) ) & maxv ) }
314+ return ret
315+ }
316+ function hexToU8 ( hex ) { const b = [ ] ; for ( let i = 0 ; i < hex . length ; i += 2 ) b . push ( parseInt ( hex . slice ( i , i + 2 ) , 16 ) ) ; return new Uint8Array ( b ) }
317+ function neventEncode ( eventId , relays , author ) {
318+ const tlv = [ ]
319+ const idBytes = hexToU8 ( eventId )
320+ tlv . push ( 0 , 32 , ...idBytes )
321+ if ( relays ) for ( const r of relays ) { const rb = new TextEncoder ( ) . encode ( r ) ; tlv . push ( 1 , rb . length , ...rb ) }
322+ if ( author ) { const ab = hexToU8 ( author ) ; tlv . push ( 2 , 32 , ...ab ) }
323+ return bech32Encode ( 'nevent' , convertBits ( tlv , 8 , 5 , true ) )
324+ }
325+
295326 function buildNoteHtml ( evt ) {
296327 const short = evt . pubkey . slice ( 0 , 8 ) + '...' + evt . pubkey . slice ( - 4 )
297328 const isMe = pubkeyHex && evt . pubkey === pubkeyHex
329+ const noteId = evt . id ? neventEncode ( evt . id , RELAYS , evt . pubkey ) : ''
330+ const timeStr = timeAgo ( evt . created_at )
331+ const timeHtml = noteId
332+ ? '<a href="https://nostr.at/' + noteId + '" target="_blank" rel="noopener" style="color:inherit;text-decoration:none" title="' + noteId + '">' + timeStr + '</a>'
333+ : timeStr
298334 return '<div class="note" data-ts="' + evt . created_at + '">' +
299335 '<div class="note-header">' +
300336 '<span class="note-pubkey">' + ( isMe ? 'you' : short ) + '</span>' +
301- '<span>' + timeAgo ( evt . created_at ) + '</span>' +
337+ '<span>' + timeHtml + '</span>' +
302338 '</div>' +
303339 '<div class="note-content">' + formatContent ( evt . content ) + '</div>' +
304340 '</div>'
0 commit comments