per guild profiles

This commit is contained in:
MathMan05 2024-12-11 21:07:59 -06:00
parent d573a06188
commit 8e7aea193c
7 changed files with 414 additions and 45 deletions

View file

@ -1440,7 +1440,7 @@ class Channel extends SnowFlake{
}
const notification = new Notification(this.notititle(message), {
body: noticontent,
icon: message.author.getpfpsrc(),
icon: message.author.getpfpsrc(this.guild),
image: imgurl,
});
notification.addEventListener("click", _=>{

View file

@ -342,6 +342,7 @@ class Guild extends SnowFlake{
}
});
}
this.perminfo ??= { channels: {} };
for(const thing of json.channels){
const temp = new Channel(thing, this);

View file

@ -141,7 +141,7 @@ type userjson = {
premium_since: string;
premium_type: number;
theme_colors: string;
pronouns: string;
pronouns?: string;
badge_ids: string[];
};
type memberjson = {
@ -149,6 +149,8 @@ type memberjson = {
id: string;
user: userjson | null;
guild_id: string;
avatar?:string;
banner?:string;
guild: {
id: string;
} | null;

View file

@ -90,6 +90,7 @@ class Localuser{
this.userinfo.username = this.user.username;
this.userinfo.id = this.user.id;
this.userinfo.pfpsrc = this.user.getpfpsrc();
this.status = this.ready.d.user_settings.status;
this.channelfocus = undefined;
this.lookingguild = undefined;
@ -477,8 +478,10 @@ class Localuser{
temp.d.guild_id ??= "@me";
const channel = this.channelids.get(temp.d.channel_id);
if(!channel)break;
const message = channel.messages.get(temp.d.message_id);
if(!message)break;
message.reactionRemove(temp.d.emoji, temp.d.user_id);
}
break;
@ -739,7 +742,7 @@ class Localuser{
for(const member of list){
const memberdiv=document.createElement("div");
const pfp=await member.user.buildstatuspfp();
const pfp=await member.user.buildstatuspfp(member);
const username=document.createElement("span");
username.classList.add("ellipsis");
username.textContent=member.name;
@ -1180,7 +1183,7 @@ class Localuser{
const pronounbox = settingsLeft.addTextInput(
I18n.getTranslation("pronouns"),
_=>{
if(newpronouns || newbio || changed){
if(newpronouns!==undefined||newbio!==undefined||changed!==undefined){
this.updateProfile({
pronouns: newpronouns,
bio: newbio,

View file

@ -4,7 +4,7 @@ import{ Guild }from"./guild.js";
import{ SnowFlake }from"./snowflake.js";
import{ memberjson, presencejson }from"./jsontypes.js";
import { I18n } from "./i18n.js";
import { Dialog } from "./settings.js";
import { Dialog, Options } from "./settings.js";
class Member extends SnowFlake{
static already = {};
@ -12,8 +12,12 @@ class Member extends SnowFlake{
user: User;
roles: Role[] = [];
nick!: string;
avatar:void|string=undefined;
banner:void|string=undefined;
private constructor(memberjson: memberjson, owner: Guild){
if(memberjson.id==="1086860370880362328"&&owner.id==="1006649183970562092"){
console.trace(memberjson);
}
super(memberjson.id);
this.owner = owner;
if(this.localuser.userMap.has(memberjson.id)){
@ -59,6 +63,251 @@ class Member extends SnowFlake{
this.user.members.delete(this.guild);
this.guild.members.delete(this);
}
getpfpsrc():string{
if(this.hypotheticalpfp&&this.avatar){
return this.avatar;
}
if(this.avatar !== undefined&&this.avatar!==null){
return`${this.info.cdn}/guilds/${this.guild.id}/users/${this.id}/avatars${
this.avatar
}.${this.avatar.startsWith("a_")?"gif":"png"}`;
}
return this.user.getpfpsrc();
}
getBannerUrl():string|undefined{
if(this.hypotheticalbanner&&this.banner){
return this.banner;
}
if(this.banner){
return `${this.info.cdn}/banners/${this.guild.id}/${
this.banner
}.${this.banner.startsWith("a_")?"gif":"png"}`;;
}else{
return undefined;
}
}
joined_at!:string;
premium_since!:string;
deaf!:boolean;
mute!:boolean;
pending!:boolean
clone(){
return new Member({
id:this.id+"#clone",
user:this.user.tojson(),
guild_id:this.guild.id,
guild:{id:this.guild.id},
avatar:this.avatar as (string|undefined),
banner:this.banner as (string|undefined),
//TODO presence
nick:this.nick,
roles:this.roles.map(_=>_.id),
joined_at:this.joined_at,
premium_since:this.premium_since,
deaf:this.deaf,
mute:this.mute,
pending:this.pending
},this.owner)
}
pronouns?:string;
bio?:string;
hypotheticalpfp=false;
hypotheticalbanner=false;
accent_color?:number;
get headers(){
return this.owner.headers;
}
updatepfp(file: Blob): void{
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = ()=>{
fetch(this.info.api + `/guilds/${this.guild.id}/members/${this.id}/`, {
method: "PATCH",
headers: this.headers,
body: JSON.stringify({
avatar: reader.result,
}),
});
};
}
updatebanner(file: Blob | null): void{
if(file){
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = ()=>{
fetch(this.info.api + `/guilds/${this.guild.id}/profile/${this.id}`, {
method: "PATCH",
headers: this.headers,
body: JSON.stringify({
banner: reader.result,
}),
});
};
}else{
fetch(this.info.api + `/guilds/${this.guild.id}/profile/${this.id}`, {
method: "PATCH",
headers: this.headers,
body: JSON.stringify({
banner: null,
}),
});
}
}
updateProfile(json: {
bio?: string|null;
pronouns?: string|null;
nick?:string|null;
}){
console.log(JSON.stringify(json));
/*
if(json.bio===""){
json.bio=null;
}
if(json.pronouns===""){
json.pronouns=null;
}
if(json.nick===""){
json.nick=null;
}
*/
fetch(this.info.api + `/guilds/${this.guild.id}/profile/${this.id}`, {
method: "PATCH",
headers: this.headers,
body: JSON.stringify(json),
});
}
editProfile(options:Options){
if(this.hasPermission("CHANGE_NICKNAME")){
const hypotheticalProfile = document.createElement("div");
let file: undefined | File | null;
let newpronouns: string | undefined;
let newbio: string | undefined;
let nick:string|undefined;
const hypomember = this.clone();
let color: string;
async function regen(){
hypotheticalProfile.textContent = "";
const hypoprofile = await hypomember.user.buildprofile(-1, -1,hypomember);
hypotheticalProfile.appendChild(hypoprofile);
}
regen();
const settingsLeft = options.addOptions("");
const settingsRight = options.addOptions("");
settingsRight.addHTMLArea(hypotheticalProfile);
const nicky=settingsLeft.addTextInput(I18n.getTranslation("member.nick:"),()=>{},{
initText:this.nick||""
});
nicky.watchForChange(_=>{
hypomember.nick=_;
nick=_;
regen();
})
const finput = settingsLeft.addFileInput(
I18n.getTranslation("uploadPfp"),
_=>{
if(file){
this.updatepfp(file);
}
},
{ clear: true }
);
finput.watchForChange(_=>{
if(!_){
file = null;
hypomember.avatar = undefined;
hypomember.hypotheticalpfp = true;
regen();
return;
}
if(_.length){
file = _[0];
const blob = URL.createObjectURL(file);
hypomember.avatar = blob;
hypomember.hypotheticalpfp = true;
regen();
}
});
let bfile: undefined | File | null;
const binput = settingsLeft.addFileInput(
I18n.getTranslation("uploadBanner"),
_=>{
if(bfile !== undefined){
this.updatebanner(bfile);
}
},
{ clear: true }
);
binput.watchForChange(_=>{
if(!_){
bfile = null;
hypomember.banner = undefined;
hypomember.hypotheticalbanner = true;
regen();
return;
}
if(_.length){
bfile = _[0];
const blob = URL.createObjectURL(bfile);
hypomember.banner = blob;
hypomember.hypotheticalbanner = true;
regen();
}
});
let changed = false;
const pronounbox = settingsLeft.addTextInput(
I18n.getTranslation("pronouns"),
_=>{
if(newpronouns!==undefined||newbio!==undefined||changed!==undefined){
this.updateProfile({
pronouns: newpronouns,
bio: newbio,
//accent_color: Number.parseInt("0x" + color.substr(1), 16),
nick
});
}
},
{ initText: this.pronouns }
);
pronounbox.watchForChange(_=>{
hypomember.pronouns = _;
newpronouns = _;
regen();
});
const bioBox = settingsLeft.addMDInput(I18n.getTranslation("bio"), _=>{}, {
initText: this.bio,
});
bioBox.watchForChange(_=>{
newbio = _;
hypomember.bio = _;
regen();
});
return;//Returns early to stop errors
if(this.accent_color){
color = "#" + this.accent_color.toString(16);
}else{
color = "transparent";
}
const colorPicker = settingsLeft.addColorInput(
I18n.getTranslation("profileColor"),
_=>{},
{ initColor: color }
);
colorPicker.watchForChange(_=>{
console.log();
color = _;
hypomember.accent_color = Number.parseInt("0x" + _.substr(1), 16);
changed = true;
regen();
});
}
}
update(memberjson: memberjson){
this.roles=[];
for(const key of Object.keys(memberjson)){
@ -114,11 +363,16 @@ class Member extends SnowFlake{
owner.members.add(memb);
return memb;
}else if(memb instanceof Promise){
return await memb; //I should do something else, though for now this is "good enough"
const member=await memb; //I should do something else, though for now this is "good enough";
if(member){
member.update(memberjson);
}
return member;
}else{
if(memberjson.presence){
memb.getPresence(memberjson.presence);
}
memb.update(memberjson);
return memb;
}
}else{

View file

@ -9,6 +9,7 @@ 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;
@ -19,7 +20,7 @@ class User extends SnowFlake{
relationshipType: 0 | 1 | 2 | 3 | 4 | 5 | 6 = 0;
bio!: MarkDown;
discriminator!: string;
pronouns!: string;
pronouns?: string;
bot!: boolean;
public_flags!: number;
accent_color!: number;
@ -57,11 +58,10 @@ class User extends SnowFlake{
}
}
clone(): User{
return new User(
{
tojson():userjson{
return {
username: this.username,
id: this.id + "#clone",
id: this.id,
public_flags: this.public_flags,
discriminator: this.discriminator,
avatar: this.avatar,
@ -74,7 +74,14 @@ class User extends SnowFlake{
theme_colors: this.theme_colors,
pronouns: this.pronouns,
badge_ids: this.badge_ids,
},
}
}
clone(): User{
const json=this.tojson();
json.id+="#clone";
return new User(
json,
this.owner
);
}
@ -189,6 +196,20 @@ class User extends SnowFlake{
return us.hasPermission("KICK_MEMBERS") || false;
}
);
this.contextmenu.addbutton(
()=>I18n.getTranslation("user.editServerProfile"),
function(this: User, member: Member | undefined){
if(!member) return;
const settings=new Settings(I18n.getTranslation("user.editServerProfile"));
member.editProfile(settings.addButton(I18n.getTranslation("user.editServerProfile"),{ltr:true}));
settings.show();
},
null,
member=>{
return !!member;
}
);
this.contextmenu.addbutton(
()=>I18n.getTranslation("user.ban"),
function(this: User, member: Member | undefined){
@ -319,19 +340,32 @@ class User extends SnowFlake{
}
}
buildpfp(): HTMLImageElement{
buildpfp(guild:Guild|void|Member|null): HTMLImageElement{
const pfp = document.createElement("img");
pfp.loading = "lazy";
pfp.src = this.getpfpsrc();
pfp.classList.add("pfp");
pfp.classList.add("userid:" + this.id);
if(guild){
(async()=>{
if(guild instanceof Guild){
const memb= await Member.resolveMember(this,guild)
if(!memb) return;
pfp.src = memb.getpfpsrc();
}else{
pfp.src = guild.getpfpsrc();
}
})();
}
return pfp;
}
async buildstatuspfp(): Promise<HTMLDivElement>{
async buildstatuspfp(guild:Guild|void|Member|null): Promise<HTMLDivElement>{
const div = document.createElement("div");
div.classList.add("pfpDiv")
const pfp = this.buildpfp();
const pfp = this.buildpfp(guild);
div.append(pfp);
const status = document.createElement("div");
status.classList.add("statusDiv");
@ -423,11 +457,19 @@ class User extends SnowFlake{
}
}
}
getpfpsrc(): string{
/**
* @param guild this is an optional thing that'll get the src of the member if it exists, otherwise ignores it, this is meant to be fast, not accurate
*/
getpfpsrc(guild:Guild|void): string{
if(this.hypotheticalpfp && this.avatar){
return this.avatar;
}
if(guild){
const member=this.members.get(guild)
if(member instanceof Member){
return member.getpfpsrc();
}
}
if(this.avatar !== null){
return`${this.info.cdn}/avatars/${this.id.replace("#clone", "")}/${
this.avatar
@ -441,12 +483,21 @@ class User extends SnowFlake{
async buildprofile(
x: number,
y: number,
guild: Guild | null = null
guild: Guild | null | Member = null
): Promise<HTMLDivElement>{
if(Contextmenu.currentmenu != ""){
Contextmenu.currentmenu.remove();
}
const membres=(async ()=>{
if(!guild) return;
let member:Member|undefined;
if(guild instanceof Guild){
member=await Member.resolveMember(this,guild)
}else{
member=guild;
}
return member;
})()
const div = document.createElement("div");
if(this.accent_color){
@ -457,20 +508,18 @@ class User extends SnowFlake{
}else{
div.style.setProperty("--accent_color", "transparent");
}
if(this.banner){
const banner = document.createElement("img");
let src: string;
if(!this.hypotheticalbanner){
src = `${this.info.cdn}/avatars/${this.id.replace("#clone", "")}/${
this.banner
}.png`;
}else{
src = this.banner;
}
banner.src = src;
banner.classList.add("banner");
const banner=this.getBanner(guild);
div.append(banner);
membres.then(member=>{
if(!member) return;
if(member.accent_color&&member.accent_color!==0){
div.style.setProperty(
"--accent_color",
`#${member.accent_color.toString(16).padStart(6, "0")}`
);
}
})
if(x !== -1){
div.style.left = `${x}px`;
div.style.top = `${y}px`;
@ -501,7 +550,7 @@ class User extends SnowFlake{
}
}
})();
const pfp = await this.buildstatuspfp();
const pfp = await this.buildstatuspfp(guild);
div.appendChild(pfp);
const userbody = document.createElement("div");
userbody.classList.add("flexttb","infosection");
@ -516,16 +565,33 @@ class User extends SnowFlake{
userbody.appendChild(discrimatorhtml);
const pronounshtml = document.createElement("p");
pronounshtml.textContent = this.pronouns;
pronounshtml.textContent = this.pronouns||"";
pronounshtml.classList.add("pronouns");
userbody.appendChild(pronounshtml);
membres.then(member=>{
if(!member) return;
if(member.pronouns&&member.pronouns!==""){
pronounshtml.textContent=member.pronouns;
}
});
const rule = document.createElement("hr");
userbody.appendChild(rule);
const biohtml = this.bio.makeHTML();
userbody.appendChild(biohtml);
membres.then(member=>{
if(!member)return;
if(member.bio&&member.bio!==""){
//TODO make markdown take Guild
userbody.insertBefore(new MarkDown(member.bio,this.localuser).makeHTML(),biohtml);
biohtml.remove();
}
});
if(guild){
Member.resolveMember(this, guild).then(member=>{
membres.then(member=>{
if(!member)return;
usernamehtml.textContent=member.name;
const roles = document.createElement("div");
@ -556,7 +622,48 @@ class User extends SnowFlake{
}
return div;
}
getBanner(guild:Guild|null|Member):HTMLImageElement{
const banner = document.createElement("img");
const bsrc=this.getBannerUrl();
if(bsrc){
banner.src = bsrc;
banner.classList.add("banner");
}
if(guild){
if(guild instanceof Member){
const bsrc=guild.getBannerUrl();
if(bsrc){
banner.src = bsrc;
banner.classList.add("banner");
}
}else{
Member.resolveMember(this,guild).then(memb=>{
if(!memb) return;
const bsrc=memb.getBannerUrl();
if(bsrc){
banner.src = bsrc;
banner.classList.add("banner");
}
})
}
}
return banner
}
getBannerUrl():string|undefined{
if(this.banner){
if(!this.hypotheticalbanner){
return `${this.info.cdn}/avatars/${this.id.replace("#clone", "")}/${
this.banner
}.png`;
}else{
return this.banner;
}
}else{
return undefined;
}
}
profileclick(obj: HTMLElement, guild?: Guild): void{
obj.onclick = (e: MouseEvent)=>{
this.buildprofile(e.clientX, e.clientY, guild);

View file

@ -396,7 +396,8 @@
"kick":"Kick member",
"ban":"Ban member",
"addRole":"Add roles",
"removeRole":"Remove roles"
"removeRole":"Remove roles",
"editServerProfile":"Edit server profile"
},
"login":{
"checking":"Checking Instance",
@ -407,7 +408,8 @@
"member":{
"kick":"Kick $1 from $2",
"reason:":"Reason:",
"ban":"Ban $1 from $2"
"ban":"Ban $1 from $2",
"nick:":"Nickname:"
},
"uploadFilesText":"Upload your files here!",
"errorReconnect":"Unable to connect to the server, retrying in **$1** seconds...",