@ -4,13 +4,13 @@ import {elem} from './utils/dom';
import { bounce } from './utils/time' ;
import { bounce } from './utils/time' ;
import { isWssUrl } from './utils/url' ;
import { isWssUrl } from './utils/url' ;
import { closeSettingsView , config , toggleSettingsView } from './settings' ;
import { closeSettingsView , config , toggleSettingsView } from './settings' ;
import { sub 24h Feed, subEventID , subNote , subProfile } from './subscriptions'
import { sub Global Feed, subEventID , subNote , subProfile , subPubkeys , subOwnContacts } from './subscriptions'
import { getReplyTo , hasEventTag , is Mention, sortByCreatedAt , sortEventCreatedAt } from './events' ;
import { getReplyTo , hasEventTag , is Event, is Mention, sortByCreatedAt , sortEventCreatedAt } from './events' ;
import { clearView , getViewContent , getViewElem , getViewOptions , setViewElem , view } from './view' ;
import { clearView , getViewContent , getViewElem , getViewOptions , setViewElem , view } from './view' ;
import { handleReaction , handleUpvote } from './reactions' ;
import { handleReaction , handleUpvote } from './reactions' ;
import { closePublishView , openWriteInput , togglePublishView } from './write' ;
import { closePublishView , openWriteInput , togglePublishView } from './write' ;
import { handleMetadata , renderProfile } from './profiles' ;
import { handleMetadata , renderProfile } from './profiles' ;
import { getContactUpdateMessage, setContactList, updateContactList } from './contacts' ;
import { followContact, getContactUpdateMessage, getContacts, resetContactList , setContactList, updateContactList , updateFollowBtn } from './contacts' ;
import { EventWithNip19 , EventWithNip19AndReplyTo , textNoteList , replyList } from './notes' ;
import { EventWithNip19 , EventWithNip19AndReplyTo , textNoteList , replyList } from './notes' ;
import { createTextNote , renderEventDetails , renderRecommendServer , renderUpdateContact } from './ui' ;
import { createTextNote , renderEventDetails , renderRecommendServer , renderUpdateContact } from './ui' ;
@ -55,7 +55,6 @@ const renderFeed = bounce(() => {
. forEach ( renderNote ) ;
. forEach ( renderNote ) ;
break ;
break ;
case 'profile' :
case 'profile' :
const isEvent = < T > ( evt? : T ) : evt is T = > evt !== undefined ;
[
[
. . . textNoteList // get notes
. . . textNoteList // get notes
. filter ( note = > note . pubkey === view . id ) ,
. filter ( note = > note . pubkey === view . id ) ,
@ -69,6 +68,20 @@ const renderFeed = bounce(() => {
renderProfile ( view . id ) ;
renderProfile ( view . id ) ;
break ;
break ;
case 'home' :
const ids = getContacts ( ) ;
[
. . . textNoteList
. filter ( note = > ids . includes ( note . pubkey ) ) ,
. . . replyList // search id in notes and replies
. filter ( reply = > ids . includes ( reply . pubkey ) )
. map ( reply = > textNoteList . find ( note = > note . id === reply . replyTo ) )
. filter ( isEvent ) ,
]
. sort ( sortByCreatedAt )
. reverse ( )
. forEach ( renderNote ) ;
break ;
case 'feed' :
case 'feed' :
const now = Math . floor ( Date . now ( ) * 0.001 ) ;
const now = Math . floor ( Date . now ( ) * 0.001 ) ;
textNoteList
textNoteList
@ -87,7 +100,7 @@ const renderFeed = bounce(() => {
const renderReply = ( evt : EventWithNip19AndReplyTo ) = > {
const renderReply = ( evt : EventWithNip19AndReplyTo ) = > {
const parent = getViewElem ( evt . replyTo ) ;
const parent = getViewElem ( evt . replyTo ) ;
if ( ! parent ) { // root article has not been rendered
if ( ! parent || getViewElem ( evt . id ) ) {
return ;
return ;
}
}
let replyContainer = parent . querySelector ( '.mbox-replies' ) ;
let replyContainer = parent . querySelector ( '.mbox-replies' ) ;
@ -110,7 +123,6 @@ const handleReply = (evt: EventWithNip19, relay: string) => {
}
}
const replyTo = getReplyTo ( evt ) ;
const replyTo = getReplyTo ( evt ) ;
if ( ! replyTo ) {
if ( ! replyTo ) {
console . warn ( 'expected to find reply-to-event-id' , evt ) ;
return ;
return ;
}
}
const evtWithReplyTo = { replyTo , . . . evt } ;
const evtWithReplyTo = { replyTo , . . . evt } ;
@ -124,7 +136,7 @@ const handleTextNote = (evt: Event, relay: string) => {
return ;
return ;
}
}
if ( eventRelayMap [ evt . id ] ) {
if ( eventRelayMap [ evt . id ] ) {
eventRelayMap [ evt . id ] = [ . . . ( eventRelayMap [ evt . id ] ) , relay ] ; // TODO: just push ?
eventRelayMap [ evt . id ] = [ . . . ( eventRelayMap [ evt . id ] ) , relay ] ; // TODO: remove eventRelayMap and just check for getViewElem ?
} else {
} else {
eventRelayMap [ evt . id ] = [ relay ] ;
eventRelayMap [ evt . id ] = [ relay ] ;
const evtWithNip19 = {
const evtWithNip19 = {
@ -145,12 +157,14 @@ const handleTextNote = (evt: Event, relay: string) => {
}
}
} ;
} ;
config . rerenderFeed = ( ) = > {
const rerenderFeed = ( ) = > {
clearView ( ) ;
clearView ( ) ;
renderFeed ( ) ;
renderFeed ( ) ;
} ;
} ;
config . rerenderFeed = rerenderFeed ;
const handleContactList = ( evt : Event , relay : string ) = > {
const handleContactList = ( evt : Event , relay : string ) = > {
// TODO: if newer and view.type === 'home' rerenderFeed()
setContactList ( evt ) ;
setContactList ( evt ) ;
const view = getViewOptions ( ) ;
const view = getViewOptions ( ) ;
if (
if (
@ -198,12 +212,12 @@ const handleRecommendServer = (evt: Event, relay: string) => {
} ;
} ;
const onEventDetails = ( evt : Event , relay : string ) = > {
const onEventDetails = ( evt : Event , relay : string ) = > {
if ( getViewElem ( ` detail- ${ evt . id } ` ) ) {
if ( getViewElem ( evt . id ) ) {
return ;
return ;
}
}
const art = renderEventDetails ( evt , relay ) ;
const art icle = renderEventDetails ( evt , relay ) ;
getViewContent ( ) . append ( art ) ;
getViewContent ( ) . append ( art icle ) ;
setViewElem ( ` detail- ${ evt . id } ` , art ) ;
setViewElem ( evt . id , art icle ) ;
} ;
} ;
const onEvent = ( evt : Event , relay : string ) = > {
const onEvent = ( evt : Event , relay : string ) = > {
@ -229,9 +243,21 @@ const onEvent = (evt: Event, relay: string) => {
// subscribe and change view
// subscribe and change view
const route = ( path : string ) = > {
const route = ( path : string ) = > {
const contactList = getContacts ( ) ;
if ( path === '/' ) {
if ( path === '/' ) {
sub24hFeed ( onEvent ) ;
if ( contactList . length ) {
view ( '/' , { type : 'feed' } ) ;
const { pubkey } = config ;
subPubkeys ( contactList , onEvent ) ;
view ( ` / ` , { type : 'home' } ) ;
} else {
subGlobalFeed ( onEvent ) ;
view ( '/feed' , { type : 'feed' } ) ;
}
return ;
}
if ( path === '/feed' ) {
subGlobalFeed ( onEvent ) ;
view ( '/feed' , { type : 'feed' } ) ;
} else if ( path . length === 64 && path . match ( /^\/[0-9a-z]+$/ ) ) {
} else if ( path . length === 64 && path . match ( /^\/[0-9a-z]+$/ ) ) {
const { type , data } = nip19 . decode ( path . slice ( 1 ) ) ;
const { type , data } = nip19 . decode ( path . slice ( 1 ) ) ;
if ( typeof data !== 'string' ) {
if ( typeof data !== 'string' ) {
@ -246,6 +272,7 @@ const route = (path: string) => {
case 'npub' :
case 'npub' :
subProfile ( data , onEvent ) ;
subProfile ( data , onEvent ) ;
view ( path , { type : 'profile' , id : data } ) ;
view ( path , { type : 'profile' , id : data } ) ;
updateFollowBtn ( data ) ;
break ;
break ;
default :
default :
console . warn ( ` type ${ type } not yet supported ` ) ;
console . warn ( ` type ${ type } not yet supported ` ) ;
@ -261,6 +288,7 @@ const route = (path: string) => {
} ;
} ;
// onload
// onload
subOwnContacts ( onEvent ) ;
route ( location . pathname ) ;
route ( location . pathname ) ;
// only push a new entry if there is no history onload
// only push a new entry if there is no history onload
@ -286,8 +314,10 @@ const handleLink = (a: HTMLAnchorElement, e: MouseEvent) => {
}
}
if (
if (
href === '/'
href === '/'
|| href . startsWith ( '/feed' )
|| href . startsWith ( '/note' )
|| href . startsWith ( '/note' )
|| href . startsWith ( '/npub' )
|| href . startsWith ( '/npub' )
|| href . length === 65
) {
) {
route ( href ) ;
route ( href ) ;
history . pushState ( { } , '' , href ) ;
history . pushState ( { } , '' , href ) ;
@ -306,17 +336,26 @@ const handleButton = (button: HTMLButtonElement) => {
case 'back' :
case 'back' :
closePublishView ( ) ;
closePublishView ( ) ;
return ;
return ;
case 'import' :
resetContactList ( config . pubkey ) ;
rerenderFeed ( ) ;
subOwnContacts ( onEvent ) ;
subGlobalFeed ( onEvent ) ;
return ;
}
}
const id = ( button . closest ( '[data-id]' ) as HTMLElement ) ? . dataset . id ;
const id = button . dataset . id || ( button . closest ( '[data-id]' ) as HTMLElement ) ? . dataset . id ;
if ( id ) {
if ( id ) {
switch ( button . name ) {
switch ( button . name ) {
case 'reply' :
case 'reply' :
openWriteInput ( button , id ) ;
openWriteInput ( button , id ) ;
break ;
return ;
case 'star' :
case 'star' :
const note = replyList . find ( r = > r . id === id ) || textNoteList . find ( n = > n . id === ( id ) ) ;
const note = replyList . find ( r = > r . id === id ) || textNoteList . find ( n = > n . id === ( id ) ) ;
note && handleUpvote ( note ) ;
note && handleUpvote ( note ) ;
break ;
return ;
case 'follow' :
followContact ( id ) ;
return ;
}
}
}
}
// const container = e.target.closest('[data-append]');
// const container = e.target.closest('[data-append]');