From ffe21e6d6c5771a781c35340c475ba2a0b4b37a3 Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Fri, 20 Dec 2024 18:59:40 -0600 Subject: [PATCH] search updates --- package.json | 1 + src/webpage/channel.ts | 12 +++++++- src/webpage/index.html | 1 + src/webpage/index.ts | 21 ++++++++++++++ src/webpage/infiniteScroller.ts | 1 - src/webpage/localuser.ts | 49 ++++++++++++++++++++++++++++++- src/webpage/markdown.ts | 51 ++++++++++++++++++++++----------- src/webpage/message.ts | 47 +++++++++++++++++------------- src/webpage/style.css | 44 ++++++++++++++++++++++++++-- src/webpage/user.ts | 1 - 10 files changed, 185 insertions(+), 43 deletions(-) diff --git a/package.json b/package.json index 6456ecb..bf586ba 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "express": "^4.19.2", "gulp-sourcemaps": "^2.6.5", "gulp-swc": "^2.2.0", + "prettier": "^3.4.2", "rimraf": "^6.0.1" }, "devDependencies": { diff --git a/src/webpage/channel.ts b/src/webpage/channel.ts index 7238c8f..9b0d429 100644 --- a/src/webpage/channel.ts +++ b/src/webpage/channel.ts @@ -777,6 +777,14 @@ class Channel extends SnowFlake{ return new Message(json[0], this); } } + async focus(id:string){ + console.time() + console.log(await this.getmessage(id)); + await this.getHTML(); + console.timeEnd() + console.warn(id); + this.infinite.focus(id); + } editLast(){ let message:Message|undefined=this.lastmessage; while(message&&message.author!==this.localuser.user){ @@ -1180,7 +1188,7 @@ class Channel extends SnowFlake{ } async buildmessages(){ this.infinitefocus = false; - this.tryfocusinfinate(); + await this.tryfocusinfinate(); } infinitefocus = false; async tryfocusinfinate(){ @@ -1548,3 +1556,5 @@ class Channel extends SnowFlake{ } Channel.setupcontextmenu(); export{ Channel }; + + diff --git a/src/webpage/index.html b/src/webpage/index.html index 1cf492e..114e917 100644 --- a/src/webpage/index.html +++ b/src/webpage/index.html @@ -60,6 +60,7 @@ Channel name + diff --git a/src/webpage/index.ts b/src/webpage/index.ts index b7fa3bb..0a8abed 100644 --- a/src/webpage/index.ts +++ b/src/webpage/index.ts @@ -204,7 +204,28 @@ import { I18n } from "./i18n.js"; if(event.key === "Enter" && !event.shiftKey) event.preventDefault(); }); markdown.giveBox(typebox); + { + const searchBox = document.getElementById("searchBox") as CustomHTMLDivElement; + const markdown = new MarkDown("", thisUser); + searchBox.markdown = markdown; + searchBox.addEventListener("keydown", event=>{ + + if(event.key === "Enter") { + event.preventDefault(); + thisUser.mSearch(markdown.rawString) + }; + }); + + markdown.giveBox(searchBox); + markdown.setCustomBox((e)=>{ + const span=document.createElement("span"); + span.textContent=e.replace("\n",""); + return span; + }); + + + } const images: Blob[] = []; const imagesHtml: HTMLElement[] = []; diff --git a/src/webpage/infiniteScroller.ts b/src/webpage/infiniteScroller.ts index 5332bb4..ac65724 100644 --- a/src/webpage/infiniteScroller.ts +++ b/src/webpage/infiniteScroller.ts @@ -37,7 +37,6 @@ offset: number this.destroyFromID(thing[1]); } this.HTMLElements=[]; - this.div=null; } constructor( getIDFromOffset: InfiniteScroller["getIDFromOffset"], diff --git a/src/webpage/localuser.ts b/src/webpage/localuser.ts index 5e953e3..7282aa6 100644 --- a/src/webpage/localuser.ts +++ b/src/webpage/localuser.ts @@ -5,7 +5,7 @@ import{ AVoice }from"./audio/voice.js"; import{ User }from"./user.js"; import{ getapiurls, SW }from"./utils/utils.js"; import { getBulkInfo, setTheme, Specialuser } from "./utils/utils.js"; -import{channeljson,emojipjson,guildjson,mainuserjson,memberjson,memberlistupdatejson,messageCreateJson,presencejson,readyjson,startTypingjson,wsjson,}from"./jsontypes.js"; +import{channeljson,emojipjson,guildjson,mainuserjson,memberjson,memberlistupdatejson,messageCreateJson,messagejson,presencejson,readyjson,startTypingjson,wsjson,}from"./jsontypes.js"; import{ Member }from"./member.js"; import{ Dialog, Form, FormError, Options, Settings }from"./settings.js"; import{ getTextNodeAtPosition, MarkDown }from"./markdown.js"; @@ -15,6 +15,7 @@ import { VoiceFactory } from "./voice.js"; import { I18n, langmap } from "./i18n.js"; import { Emoji } from "./emoji.js"; import { Play } from "./audio/play.js"; +import { Message } from "./message.js"; const wsCodesRetry = new Set([4000,4001,4002, 4003, 4005, 4007, 4008, 4009]); @@ -669,8 +670,10 @@ class Localuser{ return channel; // Add this line to return the 'channel' variable } async memberListUpdate(list:memberlistupdatejson|void){ + if(this.searching)return; const div=document.getElementById("sideDiv") as HTMLDivElement; div.innerHTML=""; + div.classList.remove("searchDiv"); const guild=this.lookingguild; if(!guild) return; const channel=this.channelfocus; @@ -832,6 +835,7 @@ class Localuser{ } } loadGuild(id: string,forceReload=false): Guild | undefined{ + this.searching=false; let guild = this.guildids.get(id); if(!guild){ guild = this.guildids.get("@me"); @@ -1968,6 +1972,49 @@ class Localuser{ } box.innerHTML=""; } + searching=false; + mSearch(query:string){ + const p=new URLSearchParams("?"); + this.searching=true; + p.set("content",query.trim()); + fetch(this.info.api+`/guilds/${this.lookingguild?.id}/messages/search/?`+p.toString(),{ + headers:this.headers + }).then(_=>_.json()).then((json:{messages:[messagejson][],total_results:number})=>{ + //FIXME total_results shall be ignored as it's known to be bad, spacebar bug. + const messages=json.messages.map(([m])=>{ + const c=this.channelids.get(m.channel_id); + if(!c) return; + if(c.messages.get(m.id)){ + return c.messages.get(m.id); + } + return new Message(m,c,true); + }).filter(_=>_!==undefined); + const sideDiv= document.getElementById("sideDiv"); + if(!sideDiv)return; + sideDiv.innerHTML=""; + sideDiv.classList.add("searchDiv"); + let channel:Channel|undefined=undefined; + for(const message of messages){ + if(channel!==message.channel){ + channel=message.channel; + const h3=document.createElement("h3"); + h3.textContent=channel.name; + h3.classList.add("channelSTitle") + sideDiv.append(h3); + } + const html=message.buildhtml(undefined,true); + html.addEventListener("click",async ()=>{ + try{ + await message.channel.focus(message.id); + }catch(e){ + console.error(e); + } + }) + sideDiv.append(html) + } + }); + } + keydown:(event:KeyboardEvent)=>unknown=()=>{}; keyup:(event:KeyboardEvent)=>boolean=()=>false; //---------- resolving members code ----------- diff --git a/src/webpage/markdown.ts b/src/webpage/markdown.ts index 5072e5c..0fef6e7 100644 --- a/src/webpage/markdown.ts +++ b/src/webpage/markdown.ts @@ -300,11 +300,7 @@ class MarkDown{ } } if( - find === count && -(count != 1 || -txt[j + 1] === " " || -txt[j + 1] === "\n" || -txt[j + 1] === undefined) + find === count &&(count != 1 ||txt[j + 1] === " " ||txt[j + 1] === "\n" ||txt[j + 1] === undefined) ){ appendcurrent(); i = j; @@ -715,16 +711,37 @@ txt[j + 1] === undefined) box.onkeyup(new KeyboardEvent("_")); }; } + customBox?:[(arg1:string)=>HTMLElement,((arg1:HTMLElement)=>string)]; + clearCustom(){ + this.customBox=undefined; + } + setCustomBox(stringToHTML:(arg1:string)=>HTMLElement,HTMLToString=MarkDown.gatherBoxText.bind(MarkDown)){ + this.customBox=[stringToHTML,HTMLToString]; + } boxupdate(offset=0){ const box=this.box.deref(); if(!box) return; - const restore = saveCaretPosition(box,offset); + let restore:undefined|(()=>void); + if(this.customBox){ + restore= saveCaretPosition(box,offset,this.customBox[1]); + }else{ + restore= saveCaretPosition(box,offset) + } box.innerHTML = ""; - box.append(this.makeHTML({ keep: true })); + if(this.customBox){ + box.append(this.customBox[0](this.rawString)); + }else{ + box.append(this.makeHTML({ keep: true })); + } if(restore){ restore(); - const test=saveCaretPosition(box); - if(test) test(); + if(this.customBox){ + const test=saveCaretPosition(box,0,this.customBox[1]); + if(test) test(); + }else{ + const test=saveCaretPosition(box); + if(test) test(); + } } this.onUpdate(text,formatted); } @@ -816,7 +833,7 @@ txt[j + 1] === undefined) //solution from https://stackoverflow.com/questions/4576694/saving-and-restoring-caret-position-for-contenteditable-div let text = ""; let formatted=false; -function saveCaretPosition(context: HTMLElement,offset=0){ +function saveCaretPosition(context: HTMLElement,offset=0,txtLengthFunc=MarkDown.gatherBoxText.bind(MarkDown)){ const selection = window.getSelection() as Selection; if(!selection)return; try{ @@ -837,7 +854,7 @@ function saveCaretPosition(context: HTMLElement,offset=0){ i++; } if(base instanceof HTMLElement){ - baseString=MarkDown.gatherBoxText(base) + baseString=txtLengthFunc(base) }else{ baseString=base.textContent as string; } @@ -855,7 +872,7 @@ function saveCaretPosition(context: HTMLElement,offset=0){ const children=[...context.childNodes]; if(children.length===1&&children[0] instanceof Text){ if(selection.containsNode(context,false)){ - build+=MarkDown.gatherBoxText(context as HTMLElement); + build+=txtLengthFunc(context as HTMLElement); }else if(selection.containsNode(context,true)){ if(context.contains(base)||context===base||base.contains(context)){ build+=baseString; @@ -871,7 +888,7 @@ function saveCaretPosition(context: HTMLElement,offset=0){ if(selection.containsNode(node,false)){ if(node instanceof HTMLElement){ - build+=MarkDown.gatherBoxText(node); + build+=txtLengthFunc(node); }else{ build+=node.textContent; } @@ -892,10 +909,10 @@ function saveCaretPosition(context: HTMLElement,offset=0){ } text=build; let len=build.length+offset; - len=Math.min(len,MarkDown.gatherBoxText(context).length) + len=Math.min(len,txtLengthFunc(context).length) return function restore(){ if(!selection)return; - const pos = getTextNodeAtPosition(context, len); + const pos = getTextNodeAtPosition(context, len,txtLengthFunc); selection.removeAllRanges(); const range = new Range(); range.setStart(pos.node, pos.position); @@ -906,7 +923,7 @@ function saveCaretPosition(context: HTMLElement,offset=0){ } } -function getTextNodeAtPosition(root: Node, index: number):{ +function getTextNodeAtPosition(root: Node, index: number,txtLengthFunc=MarkDown.gatherBoxText.bind(MarkDown)):{ node: Node, position: number, }{ @@ -931,7 +948,7 @@ function getTextNodeAtPosition(root: Node, index: number):{ lastElm=node; let len:number if(node instanceof HTMLElement){ - len=MarkDown.gatherBoxText(node).length; + len=txtLengthFunc(node).length; }else{ len=(node.textContent as string).length } diff --git a/src/webpage/message.ts b/src/webpage/message.ts index 27d632b..2d5bb5e 100644 --- a/src/webpage/message.ts +++ b/src/webpage/message.ts @@ -101,12 +101,14 @@ class Message extends SnowFlake{ if(prev) prev.generateMessage(); this.generateMessage(undefined,false) } - constructor(messagejson: messagejson, owner: Channel){ + constructor(messagejson: messagejson, owner: Channel,dontStore=false){ super(messagejson.id); this.owner = owner; this.headers = this.owner.headers; this.giveData(messagejson); - this.owner.messages.set(this.id, this); + if(!dontStore){ + this.owner.messages.set(this.id, this); + } } reactionToggle(emoji: string | Emoji){ let remove = false; @@ -184,8 +186,11 @@ class Message extends SnowFlake{ } if(this.div){ this.generateMessage(); + return; + } + if(+this.id>+(this.channel.lastmessageid||"0")){ + func(); } - func(); } canDelete(){ return( @@ -338,15 +343,16 @@ class Message extends SnowFlake{ this.generateMessage(); } } - generateMessage(premessage?: Message | undefined, ignoredblock = false){ - if(!this.div)return; + generateMessage(premessage?: Message | undefined, ignoredblock = false,dupe:false|HTMLDivElement=false){ + const div = dupe||this.div; + if(!div)return; + const editmode=this.channel.editing===this; - if(!premessage){ + if(!premessage&&!dupe){ premessage = this.channel.messages.get( this.channel.idToPrev.get(this.id) as string ); } - const div = this.div; for(const user of this.mentions){ if(user === this.localuser.user){ div.classList.add("mentioned"); @@ -390,14 +396,10 @@ class Message extends SnowFlake{ build.classList.add("blocked", "topMessage"); const span = document.createElement("span"); let count = 1; - let next = this.channel.messages.get( - this.channel.idToNext.get(this.id) as string - ); + let next = this.channel.messages.get(this.channel.idToNext.get(this.id) as string); while(next?.author === this.author){ count++; - next = this.channel.messages.get( - this.channel.idToNext.get(next.id) as string - ); + next = this.channel.messages.get(this.channel.idToNext.get(next.id) as string); } span.textContent = I18n.getTranslation("showBlockedMessages",count+""); build.append(span); @@ -620,12 +622,14 @@ class Message extends SnowFlake{ text.append(time); div.classList.add("topMessage"); } - const reactions = document.createElement("div"); - reactions.classList.add("flexltr", "reactiondiv"); - this.reactdiv = new WeakRef(reactions); - this.updateReactions(); - div.append(reactions); - this.bindButtonEvent(); + if(!dupe){ + const reactions = document.createElement("div"); + reactions.classList.add("flexltr", "reactiondiv"); + this.reactdiv = new WeakRef(reactions); + this.updateReactions(); + div.append(reactions); + this.bindButtonEvent(); + } return div; } bindButtonEvent(){ @@ -829,7 +833,10 @@ class Message extends SnowFlake{ } } } - buildhtml(premessage?: Message | undefined): HTMLElement{ + buildhtml(premessage?: Message | undefined,dupe=false): HTMLElement{ + if(dupe){ + return this.generateMessage(premessage,false,document.createElement("div")) as HTMLElement; + } if(this.div){ console.error(`HTML for ${this.id} already exists, aborting`); return this.div; diff --git a/src/webpage/style.css b/src/webpage/style.css index ffeaef8..380c1b3 100644 --- a/src/webpage/style.css +++ b/src/webpage/style.css @@ -61,6 +61,10 @@ body { flex-grow: 1; min-height: 0; } +.channelSTitle{ + margin-top:.2in; + margin-bottom:0; +} p, h1, h2, h3, pre, form { margin: 0; } @@ -957,6 +961,26 @@ span.instanceStatus { display:flex; flex-direction: row; } + +.searchBox:empty { + width:2in; +} +.searchBox { + white-space: nowrap; + height: .075in; + padding: 7px 10px 13px 10px; + background: var(--dock-bg); + border-radius: 4px; + /* overflow-y: auto; */ + display:flex; + flex-direction: row; + width: 3in; + margin: 0 .1in; + overflow: hidden; + margin-left: auto; + flex-shrink: 0; + transition: width .2s; +} .outerTypeBox > span::before { content: "\feff"; } @@ -1385,7 +1409,21 @@ img.bigembedimg { .acceptinvbutton:hover, .acceptinvbutton:disabled { background: color-mix(in hsl, var(--green) 80%, var(--black)); } - +#sideDiv.searchDiv{ + width: 30vw; + .topMessage{ + margin-top:2px; + margin-bottom:10px; + cursor:pointer; + padding:.05in; + border-radius:.075in; + background:#00000020; + } + .topMessage:hover{ + background:#00000050; + + } +} /* Sidebar */ #sideDiv { display: none; @@ -1396,6 +1434,7 @@ img.bigembedimg { overflow-y: auto; box-sizing: border-box; } + .memberList { padding-bottom: 16px; color: var(--primary-text-soft); @@ -1416,8 +1455,9 @@ img.bigembedimg { #memberlisttoggleicon { display: block; padding: 12px 0; - margin-left: auto; + margin-left: 0; cursor: pointer; + flex-grow: 0; } #memberlisttoggleicon span { height: 16px; diff --git a/src/webpage/user.ts b/src/webpage/user.ts index 6401446..475bf0c 100644 --- a/src/webpage/user.ts +++ b/src/webpage/user.ts @@ -9,7 +9,6 @@ import { Role } from "./role.js"; import { Search } from "./search.js"; import { I18n } from "./i18n.js"; import { Direct } from "./direct.js"; -import { Settings } from "./settings.js"; class User extends SnowFlake{ owner: Localuser;