Reaction support

This commit is contained in:
MathMan05 2024-08-05 21:47:40 -05:00
parent 8b3fe48a74
commit 24bdde1968
12 changed files with 458 additions and 51 deletions

View file

@ -11,8 +11,7 @@ import { Role } from "./role.js";
import {InfiniteScroller} from "./infiniteScroller.js";
import { SnowFlake } from "./snowflake.js";
import { channeljson, messagejson, readyjson } from "./jsontypes.js";
import {Emoji} from "./emoji.js";
new Emoji();
declare global {
interface NotificationOptions {
image?: string

View file

@ -1,7 +1,7 @@
class Contextmenu{
static currentmenu;
name:string;
buttons:[string,Function,string,Function,Function][];
buttons:[string,Function,string,Function,Function,string][];
div:HTMLDivElement;
static setup(){
Contextmenu.currentmenu="";
@ -20,7 +20,11 @@ class Contextmenu{
this.buttons=[]
}
addbutton(text:string,onclick:Function,img=null,shown=_=>true,enabled=_=>true){
this.buttons.push([text,onclick,img,shown,enabled])
this.buttons.push([text,onclick,img,shown,enabled,"button"])
return {};
}
addsubmenu(text:string,onclick:(e:MouseEvent)=>void,img=null,shown=_=>true,enabled=_=>true){
this.buttons.push([text,onclick,img,shown,enabled,"submenu"])
return {};
}
makemenu(x:number,y:number,addinfo:any,obj:HTMLElement){
@ -36,7 +40,12 @@ class Contextmenu{
intext.textContent=thing[0]
textb.appendChild(intext)
console.log(thing)
intext.onclick=thing[1].bind(addinfo,obj);
if(thing[5]==="button"){
intext.onclick=thing[1].bind(addinfo,obj);
}else if(thing[5]==="submenu"){
intext.onclick=thing[1].bind(addinfo);
}
div.appendChild(textb);
}
if(Contextmenu.currentmenu!=""){

View file

@ -1,3 +1,5 @@
import { Contextmenu } from "./contextmenu.js";
class Emoji{
static emojis:{
name:string,
@ -70,6 +72,65 @@ class Emoji{
Emoji.decodeEmojiList(e);
})
}
static async emojiPicker(x:number,y:number):Promise<Emoji|string>{
let res:(r:Emoji|string)=>void;
const promise=new Promise((r)=>{res=r;})
const menu=document.createElement("div");
menu.classList.add("flextttb", "emojiPicker")
menu.style.top=y+"px";
menu.style.left=x+"px";
setTimeout(()=>{
if(Contextmenu.currentmenu!=""){
Contextmenu.currentmenu.remove();
}
document.body.append(menu);
Contextmenu.currentmenu=menu;
Contextmenu.keepOnScreen(menu);
},10)
const title=document.createElement("h2");
title.textContent=Emoji.emojis[0].name;
title.classList.add("emojiTitle");
menu.append(title);
console.log("menu :3");
const selection=document.createElement("div");
selection.classList.add("flexltr");
console.log("menu :3");
const body=document.createElement("div");
body.classList.add("emojiBody");
let i=0;
for(const thing of Emoji.emojis){
const select=document.createElement("div");
select.textContent=thing.emojis[0].emoji;
select.classList.add("emojiSelect");
selection.append(select);
const clickEvent=()=>{
title.textContent=thing.name;
body.innerHTML="";
for(const emojit of thing.emojis){
const emoji=document.createElement("div");
emoji.classList.add("emojiSelect");
emoji.textContent=emojit.emoji;
body.append(emoji);
emoji.onclick=_=>{
res(emojit.emoji);
Contextmenu.currentmenu.remove();
}
}
};
select.onclick=clickEvent
if(i===0){
clickEvent();
}
i++;
}
menu.append(selection);
menu.append(body);
console.log("menu :3");
return promise;
}
}
Emoji.grabEmoji();
export {Emoji};

View file

@ -271,7 +271,13 @@ type messagejson={
mention_roles: [], //need examples to fix
attachments: filejson[],
embeds: embedjson[],
reactions: [], //ToDo
reactions: {
count:number,
emoji:{
name:string
},//very likely needs expanding
me:boolean,
}[],
nonce: string,
pinned: boolean,
type: number

View file

@ -8,6 +8,7 @@ import {setTheme, Specialuser} from "./login.js";
import { SnowFlake } from "./snowflake.js";
import { Message } from "./message.js";
import { channeljson, guildjson, memberjson, readyjson, userjson } from "./jsontypes.js";
import { Member } from "./member.js";
const wsCodesRetry=new Set([4000,4003,4005,4007,4008,4009]);
@ -291,7 +292,24 @@ class Localuser{
this.guilds.push(guildy);
this.guildids.set(guildy.id,guildy);
document.getElementById("servers").insertBefore(guildy.generateGuildIcon(),document.getElementById("bottomseparator"));
break;
}
case "MESSAGE_REACTION_ADD":
if(SnowFlake.hasSnowFlakeFromID(temp.d.message_id,Message)){
const message=SnowFlake.getSnowFlakeFromID(temp.d.message_id,Message).getObject();
const guild=SnowFlake.getSnowFlakeFromID(temp.d.guild_id,Guild).getObject();
message.giveReaction(temp.d.emoji,new Member(temp.d.member,guild));
}
break;
case "MESSAGE_REACTION_REMOVE":
if(SnowFlake.hasSnowFlakeFromID(temp.d.message_id,Message)){
const message=SnowFlake.getSnowFlakeFromID(temp.d.message_id,Message).getObject();
const guild=SnowFlake.getSnowFlakeFromID(temp.d.guild_id,Guild).getObject();
console.log("test");
message.takeReaction(temp.d.emoji,temp.d.user_id);
}
break;
}
}else if(temp.op===10){

View file

@ -9,6 +9,8 @@ import { Role } from "./role.js";
import {File} from "./file.js";
import { SnowFlake } from "./snowflake.js";
import { messagejson } from "./jsontypes.js";
import {Emoji} from "./emoji.js";
new Emoji();
class Message{
static contextmenu=new Contextmenu("message menu");
@ -28,6 +30,7 @@ class Message{
static resolve:Function;
div:HTMLDivElement;
member:Member;
reactions:messagejson["reactions"];
get id(){
return this.snowflake.id;
}
@ -51,6 +54,12 @@ class Message{
Message.contextmenu.addbutton("Copy message id",function(){
navigator.clipboard.writeText(this.id);
});
Message.contextmenu.addsubmenu("Add reaction",function(this:Message,e){
Emoji.emojiPicker(e.x,e.y).then(_=>{
console.log(_,":3")
this.reactionToggle(_);
});
});
Message.contextmenu.addbutton("Edit",function(){
this.channel.editing=this;
const markdown=(document.getElementById("typebox"))["markdown"] as MarkDown;
@ -67,6 +76,24 @@ class Message{
this.giveData(messagejson);
}
reactionToggle(emoji:string|Emoji){
if(typeof emoji === "string"){
let remove=false;
for(const thing of this.reactions){
if(thing.emoji.name===emoji){
remove=thing.me;
break;
}
}
fetch(this.info.api.toString()+ "/channels/"+this.channel.id+"/messages/"+this.id+"/reactions/"+encodeURIComponent(emoji)+"/@me",{
method:remove?"DELETE":"PUT",
headers:this.headers,
})
}else{
emoji
}
}
giveData(messagejson:messagejson){
for(const thing of Object.keys(messagejson)){
if(thing==="attachments"){
@ -94,7 +121,9 @@ class Message{
}
this[thing]=messagejson[thing];
}
if(messagejson.reactions?.length){
console.log(messagejson.reactions,":3");
}
this.author=new User(messagejson.author,this.localuser);
for(const thing in messagejson.mentions){
@ -187,6 +216,7 @@ class Message{
this.channel.lastmessage=prev.getObject();
}
}
reactdiv:WeakRef<HTMLDivElement>;
generateMessage(premessage:Message=null){
if(!premessage){
premessage=this.channel.idToPrev.get(this.snowflake)?.getObject();
@ -210,27 +240,6 @@ class Message{
const reply=document.createElement("div");
username.classList.add("username");
this.author.bind(username,this.guild);
/*
Member.resolve(this.author,this.guild).then(_=>{
if(!_) {return};
console.log(_.error);
if(_.error){
username.textContent+="Error";
alert("Should've gotten here")
const error=document.createElement("span");
error.textContent="!";
error.classList.add("membererror");
username.after(error);
return;
}
username.style.color=_.getColor();
}).catch(_=>{
console.log(_)
});
*/
reply.classList.add("replytext");
replyline.appendChild(reply);
const line2=document.createElement("hr");
@ -360,8 +369,76 @@ class Message{
}
div["all"]=this;
const reactions=document.createElement("div");
reactions.classList.add("flexltr","reactiondiv");
this.reactdiv=new WeakRef(reactions);
this.updateReactions();
div.append(reactions)
return(div)
}
updateReactions(){
const reactdiv=this.reactdiv.deref();
if(!reactdiv) return;
reactdiv.innerHTML="";
for(const thing of this.reactions){
console.log(thing,":3")
const reaction=document.createElement("div");
reaction.classList.add("reaction");
if(thing.me){
reaction.classList.add("meReacted")
}
const emoji=document.createElement("p");
emoji.textContent=thing.emoji.name;
reaction.append(emoji);
const count=document.createElement("p");
count.textContent=""+thing.count;
count.classList.add("reactionCount");
reaction.append(count);
reactdiv.append(reaction);
reaction.onclick=_=>{
this.reactionToggle(thing.emoji.name);
}
}
}
giveReaction(data:{name:string},member:Member){
for(const thing of this.reactions){
if(thing.emoji.name===data.name){
thing.count++;
if(member.id===this.localuser.user.id){
thing.me=true;
this.updateReactions();
return;
}
}
}
this.reactions.push({
count:1,
emoji:data,
me:member.id===this.localuser.user.id
});
this.updateReactions();
}
takeReaction(data:{name:string},id:string){
console.log("test");
for(const i in this.reactions){
const thing=this.reactions[i];
console.log(thing,data);
if(thing.emoji.name===data.name){
thing.count--;
if(thing.count===0){
this.reactions.splice(+i,1);
this.updateReactions();
return;
}
if(id===this.localuser.user.id){
thing.me=false;
this.updateReactions();
return;
}
}
}
}
buildhtml(premessage:Message,del:Promise<void>=Message.del){
if(this.div){console.error(`HTML for ${this.snowflake} already exists, aborting`);return;}
//premessage??=messages.lastChild;

View file

@ -1233,6 +1233,7 @@ span {
flex-direction: row;
max-height: 100%;
overflow: auto;
flex-shrink: 0;
}
.flexttb{
display: flex;
@ -1535,4 +1536,79 @@ form div{
.svgicon{
width:.5in;
height:.5in;
}
.emojiPicker{
position:absolute;
width:3.5in;
height:4in;
background:var(--profile-info-bg);
border-radius:.15in;
border:solid var(--black) .03in;
box-shadow:black .03in .03in .2in;
display: flex;
flex-direction: column;
}
.emojiSelect{
font-size:.23in;
text-align: center;
cursor:pointer;
width:.4in;
height:.4in;
background:color-mix(in hsl, var(--profile-info-bg),black);
border-radius:2in;
display:flex;
flex-direction: column;
justify-content: space-evenly;
border:solid var(--black) .03in;
flex-shrink:0;
}
.emojiTitle{
padding: .03in;
padding-left: .1in;
border-bottom: solid var(--black);
width: 100%;
box-sizing: border-box;
margin-bottom: .025in;
}
.emojiBody{
display:flex;
flex-wrap: wrap;
flex-direction: row;
align-content: stretch;
justify-content: space-evenly;
/* height: 100%; */
overflow-y: scroll;
box-sizing: border-box;
flex-shrink: 1;
flex-grow: 1;
}
.reactionCount{
position:absolute;
font-size:.125in;
padding:0 .01in;
bottom:-.03in;
left:-.03in;
background: var(--channels-bg);
border:solid var(--black) .021in;
border-radius:.05in;
}
.reactiondiv{
}
.reaction{
width:.25in;
position:relative;
height:.25in;
display:flex;
flex-direction: row;
align-content: stretch;
background: var(--profile-info-bg);
border-radius:.05in;
justify-content: space-evenly;
border:solid var(--black);
margin-right:.05in;
p{
flex-grow:0;
flex-shrink:0;
}
}