class InfiniteScroller{ readonly getIDFromOffset:(ID:string,offset:number)=>Promise; readonly getHTMLFromID:(ID:string)=>Promise; readonly destroyFromID:(ID:string)=>Promise; readonly reachesBottom:()=>void; private readonly minDist=3000; private readonly maxDist=8000; HTMLElements:[HTMLElement,string][]=[]; div:HTMLDivElement; scroll:HTMLDivElement; constructor(getIDFromOffset:InfiniteScroller["getIDFromOffset"],getHTMLFromID:InfiniteScroller["getHTMLFromID"],destroyFromID:InfiniteScroller["destroyFromID"],reachesBottom:InfiniteScroller["reachesBottom"]=()=>{}){ this.getIDFromOffset=getIDFromOffset; this.getHTMLFromID=getHTMLFromID; this.destroyFromID=destroyFromID; this.reachesBottom=reachesBottom; } interval:NodeJS.Timeout; async getDiv(initialId:string,bottom=true):Promise{ const div=document.createElement("div"); div.classList.add("messagecontainer"); //div.classList.add("flexttb") const scroll=document.createElement("div"); scroll.classList.add("flexttb","scroller") div.append(scroll); this.div=div; this.interval=setInterval(this.updatestuff.bind(this),100); this.scroll=scroll; this.scroll.addEventListener("scroll",this.watchForChange.bind(this)); new ResizeObserver(this.watchForChange.bind(this)).observe(div); new ResizeObserver(this.watchForChange.bind(this)).observe(scroll); await this.firstElement(initialId) this.updatestuff(); await this.watchForChange().then(_=>{ this.updatestuff(); }) return div; } scrollBottom:number; scrollTop:number; updatestuff(){ this.scrollBottom = this.scroll.scrollHeight - this.scroll.scrollTop - this.scroll.clientHeight; this.scrollTop=this.scroll.scrollTop; if(this.scrollBottom){ this.reachesBottom(); } //this.watchForChange(); } async firstElement(id:string){ const html=await this.getHTMLFromID(id); this.scroll.append(html); this.HTMLElements.push([html,id]); } currrunning:boolean=false; async addedBottom(){ this.updatestuff(); const scrollBottom=this.scrollBottom; await this.watchForChange(); if(scrollBottom<30){ this.scroll.scrollTop=this.scroll.scrollHeight; } } async watchForChange():Promise{ if(this.currrunning){ return; }else{ this.currrunning=true; } let again=false; if(!this.div){this.currrunning=false;return} /* if(this.scrollTop===0){ this.scrollTop=10; } */ if(this.scrollTop===0){ this.scrollTop=1; this.scroll.scrollTop=1; } if(this.scrollTopthis.maxDist){ again=true; const html=this.HTMLElements.shift(); await this.destroyFromID(html[1]); this.scrollTop-=60; } const scrollBottom = this.scrollBottom; if(scrollBottomthis.maxDist){ again=true; const html=this.HTMLElements.pop(); await this.destroyFromID(html[1]); this.scrollBottom-=60; } this.currrunning=false; if(again){ await this.watchForChange(); } this.currrunning=false; } async focus(id:string,flash=true){ let element:HTMLElement; for(const thing of this.HTMLElements){ if(thing[1]===id){ element=thing[0]; } } console.log(element,id,":3"); if(element){ element.scrollIntoView(); if(flash){ element.classList.remove("jumped"); await new Promise(resolve => setTimeout(resolve, 100)); element.classList.add("jumped"); } }else{ for(const thing of this.HTMLElements){ await this.destroyFromID(thing[1]); } this.HTMLElements=[]; await this.firstElement(id); this.updatestuff(); await this.watchForChange(); await new Promise(resolve => setTimeout(resolve, 100)); await this.focus(id,true); } } async delete():Promise{ for(const thing of this.HTMLElements){ await this.destroyFromID(thing[1]); } this.HTMLElements=[]; clearInterval(this.interval); if(this.div){ this.div.remove(); } this.scroll=null; this.div=null; } } export {InfiniteScroller};