improve the dev portal

This commit is contained in:
MathMan05
2024-09-24 20:25:52 -05:00
parent 9ae7a9c71f
commit 5abe4c6748
2 changed files with 246 additions and 356 deletions

View File

@@ -16,7 +16,7 @@ import{
wsjson, wsjson,
}from"./jsontypes.js"; }from"./jsontypes.js";
import{ Member }from"./member.js"; import{ Member }from"./member.js";
import{ FormError, Settings }from"./settings.js"; import{ Form, FormError, Options, Settings }from"./settings.js";
import{ MarkDown }from"./markdown.js"; import{ MarkDown }from"./markdown.js";
const wsCodesRetry = new Set([4000, 4003, 4005, 4007, 4008, 4009]); const wsCodesRetry = new Set([4000, 4003, 4005, 4007, 4008, 4009]);
@@ -1286,9 +1286,9 @@ class Localuser{
{ {
const devPortal = settings.addButton("Developer Portal"); const devPortal = settings.addButton("Developer Portal");
const teamsRes = await fetch(this.info.api + "/teams", { fetch(this.info.api + "/teams", {
headers: this.headers, headers: this.headers,
}); }).then(async (teamsRes)=>{
const teams = await teamsRes.json(); const teams = await teamsRes.json();
devPortal.addButtonInput("", "Create application", ()=>{ devPortal.addButtonInput("", "Create application", ()=>{
@@ -1352,158 +1352,46 @@ class Localuser{
} }
const name = document.createElement("h2"); const name = document.createElement("h2");
name.textContent = name.textContent = application.name + (application.bot ? " (Bot)" : "");
application.name + (application.bot ? " (Bot)" : "");
container.appendChild(name); container.appendChild(name);
container.addEventListener("click", async ()=>{ container.addEventListener("click", async ()=>{
this.manageApplication(application.id); this.manageApplication(application.id,devPortal);
}); });
appListContainer.appendChild(container); appListContainer.appendChild(container);
} }
); );
}); });
devPortal.addHTMLArea(appListContainer); devPortal.addHTMLArea(appListContainer);
});
} }
settings.show(); settings.show();
} }
async manageApplication(appId = ""){ readonly botTokens:Map<string,string>=new Map();
async manageApplication(appId = "", container:Options){
const res = await fetch(this.info.api + "/applications/" + appId, { const res = await fetch(this.info.api + "/applications/" + appId, {
headers: this.headers, headers: this.headers,
}); });
const json = await res.json(); const json = await res.json();
const fields: any = {}; const fields: any = {};
const appDialog = new Dialog([ const form=container.addSubForm(json.name,()=>{},{
"vdiv", fetchURL:this.info.api + "/applications/" + appId,
["title", "Editing " + json.name],
[
"vdiv",
[
"textbox",
"Application name:",
json.name,
(event: Event)=>{
const target = event.target as HTMLInputElement;
fields.name = target.value;
},
],
[
"mdbox",
"Description:",
json.description,
(event: Event)=>{
const target = event.target as HTMLInputElement;
fields.description = target.value;
},
],
[
"vdiv",
json.icon
? [
"img",
this.info.cdn +
"/app-icons/" +
appId +
"/" +
json.icon +
".png?size=128",
[128, 128],
]
: ["text", "No icon"],
[
"fileupload",
"Application icon:",
event=>{
const reader = new FileReader();
const files = (event.target as HTMLInputElement).files;
if(files){
reader.readAsDataURL(files[0]);
reader.onload = ()=>{
fields.icon = reader.result;
};
}
},
],
],
],
[
"hdiv",
[
"textbox",
"Privacy policy URL:",
json.privacy_policy_url || "",
(event: Event)=>{
const target = event.target as HTMLInputElement;
fields.privacy_policy_url = target.value;
},
],
[
"textbox",
"Terms of Service URL:",
json.terms_of_service_url || "",
(event: Event)=>{
const target = event.target as HTMLInputElement;
fields.terms_of_service_url = target.value;
},
],
],
[
"hdiv",
[
"checkbox",
"Make bot publicly inviteable?",
json.bot_public,
(event: Event)=>{
const target = event.target as HTMLInputElement;
fields.bot_public = target.checked;
},
],
[
"checkbox",
"Require code grant to invite the bot?",
json.bot_require_code_grant,
(event: Event)=>{
const target = event.target as HTMLInputElement;
fields.bot_require_code_grant = target.checked;
},
],
],
[
"hdiv",
[
"button",
"",
"Save changes",
async ()=>{
const updateRes = await fetch(
this.info.api + "/applications/" + appId,
{
method:"PATCH", method:"PATCH",
headers: this.headers, headers:this.headers
body: JSON.stringify(fields), });
} form.addTextInput("Application name:","name",{initText:json.name});
); form.addMDInput("Description:","description",{initText:json.description});
if(updateRes.ok) appDialog.hide(); form.addFileInput("Icon:","icon");
else{ form.addTextInput("Privacy policy URL:","privacy_policy_url",{initText:json.privacy_policy_url});
const updateJSON = await updateRes.json(); form.addTextInput("Terms of Service URL:","terms_of_service_url",{initText:json.terms_of_service_url});
alert("An error occurred: " + updateJSON.message); form.addCheckboxInput("Make bot publicly inviteable?","bot_public",{initState:json.bot_public});
} form.addCheckboxInput("Require code grant to invite the bot?","bot_require_code_grant",{initState:json.bot_require_code_grant});
}, form.addButtonInput("",(json.bot ? "Manage" : "Add")+" bot",async ()=>{
],
[
"button",
"",
(json.bot ? "Manage" : "Add") + " bot",
async ()=>{
if(!json.bot){ if(!json.bot){
if( if(!confirm("Are you sure you want to add a bot to this application? There's no going back.")){
!confirm(
"Are you sure you want to add a bot to this application? There's no going back."
)
)
return; return;
}
const updateRes = await fetch( const updateRes = await fetch(
this.info.api + "/applications/" + appId + "/bot", this.info.api + "/applications/" + appId + "/bot",
{ {
@@ -1512,108 +1400,30 @@ class Localuser{
} }
); );
const updateJSON = await updateRes.json(); const updateJSON = await updateRes.json();
alert("Bot token:\n" + updateJSON.token); this.botTokens.set(appId,updateJSON.token);
} }
this.manageBot(appId,form);
appDialog.hide(); })
this.manageBot(appId);
},
],
],
]);
appDialog.show();
} }
async manageBot(appId = ""){ async manageBot(appId = "",container:Form){
const res = await fetch(this.info.api + "/applications/" + appId, { const res = await fetch(this.info.api + "/applications/" + appId, {
headers: this.headers, headers: this.headers,
}); });
const json = await res.json(); const json = await res.json();
if(!json.bot) if(!json.bot){
return alert( return alert("For some reason, this application doesn't have a bot (yet).");
"For some reason, this application doesn't have a bot (yet)."
);
const fields: any = {
username: json.bot.username,
avatar: json.bot.avatar
? this.info.cdn +
"/app-icons/" +
appId +
"/" +
json.bot.avatar +
".png?size=256"
: "",
};
const botDialog = new Dialog([
"vdiv",
["title", "Editing bot: " + json.bot.username],
[
"hdiv",
[
"textbox",
"Bot username:",
json.bot.username,
(event: Event)=>{
const target = event.target as HTMLInputElement;
fields.username = target.value;
},
],
[
"vdiv",
fields.avatar
? ["img", fields.avatar, [128, 128]]
: ["text", "No avatar"],
[
"fileupload",
"Bot avatar:",
event=>{
const reader = new FileReader();
const files = (event.target as HTMLInputElement).files;
if(files){
const file = files[0];
reader.readAsDataURL(file);
reader.onload = ()=>{
fields.avatar = reader.result;
};
} }
}, const form=container.addSubForm("Editing bot "+json.bot.username,()=>{},{
],
],
],
[
"hdiv",
[
"button",
"",
"Save changes",
async ()=>{
const updateRes = await fetch(
this.info.api + "/applications/" + appId + "/bot",
{
method:"PATCH", method:"PATCH",
headers: this.headers, fetchURL:this.info.api + "/applications/" + appId + "/bot",
body: JSON.stringify(fields), headers:this.headers
} });
); form.addTextInput("Bot username:","username",{initText:json.bot.username});
if(updateRes.ok) botDialog.hide(); form.addFileInput("Bot avatar:","avatar");
else{ form.addButtonInput("Reset Token:","Reset",async ()=>{
const updateJSON = await updateRes.json(); if(!confirm("Are you sure you want to reset the bot token? Your bot will stop working until you update it.")){
alert("An error occurred: " + updateJSON.message);
}
},
],
[
"button",
"",
"Reset token",
async ()=>{
if(
!confirm(
"Are you sure you want to reset the bot token? Your bot will stop working until you update it."
)
)
return; return;
}
const updateRes = await fetch( const updateRes = await fetch(
this.info.api + "/applications/" + appId + "/bot/reset", this.info.api + "/applications/" + appId + "/bot/reset",
{ {
@@ -1622,13 +1432,10 @@ class Localuser{
} }
); );
const updateJSON = await updateRes.json(); const updateJSON = await updateRes.json();
alert("New token:\n" + updateJSON.token); text.setText("Token: "+updateJSON.token);
botDialog.hide(); this.botTokens.set(appId,updateJSON.token);
}, });
], const text=form.addText(this.botTokens.has(appId)?"Token: "+this.botTokens.get(appId):"Token: *****************")
],
]);
botDialog.show();
} }
//---------- resolving members code ----------- //---------- resolving members code -----------

View File

@@ -131,14 +131,24 @@ class SettingsText implements OptionsElement<void>{
readonly onSubmit!: (str: string) => void; readonly onSubmit!: (str: string) => void;
value!: void; value!: void;
readonly text: string; readonly text: string;
elm!:WeakRef<HTMLSpanElement>;
constructor(text: string){ constructor(text: string){
this.text = text; this.text = text;
} }
generateHTML(): HTMLSpanElement{ generateHTML(): HTMLSpanElement{
const span = document.createElement("span"); const span = document.createElement("span");
span.innerText = this.text; span.innerText = this.text;
this.elm=new WeakRef(span);
return span; return span;
} }
setText(text:string){
if(this.elm){
const span=this.elm.deref();
if(span){
span.innerText=text;
}
}
}
watchForChange(){} watchForChange(){}
submit(){} submit(){}
} }
@@ -516,17 +526,26 @@ class Options implements OptionsElement<void>{
return options; return options;
} }
subOptions: Options | Form | undefined; subOptions: Options | Form | undefined;
genTop(){
const container = this.container.deref();
if(container){
if(this.isTop()){
this.generateContainter();
}else if(this.owner instanceof Options){
this.owner.genTop();
}else{
(this.owner as Form).owner.genTop();
}
}else{
throw new Error(
"Tried to make a sub menu when the options weren't rendered"
);
}
}
addSubOptions(name: string, { ltr = false } = {}){ addSubOptions(name: string, { ltr = false } = {}){
const options = new Options(name, this, { ltr }); const options = new Options(name, this, { ltr });
this.subOptions = options; this.subOptions = options;
const container = this.container.deref(); this.genTop();
if(container){
this.generateContainter();
}else{
throw new Error(
"Tried to make a subOptions when the options weren't rendered"
);
}
return options; return options;
} }
addSubForm( addSubForm(
@@ -550,19 +569,12 @@ class Options implements OptionsElement<void>{
traditionalSubmit, traditionalSubmit,
}); });
this.subOptions = options; this.subOptions = options;
const container = this.container.deref(); this.genTop();
if(container){
this.generateContainter();
}else{
throw new Error(
"Tried to make a subForm when the options weren't rendered"
);
}
return options; return options;
} }
returnFromSub(){ returnFromSub(){
this.subOptions = undefined; this.subOptions = undefined;
this.generateContainter(); this.genTop();
} }
addSelect( addSelect(
label: string, label: string,
@@ -710,36 +722,70 @@ class Options implements OptionsElement<void>{
div.append(container); div.append(container);
return div; return div;
} }
generateContainter(){ generateName():(HTMLElement|string)[]{
const container = this.container.deref(); const build:(HTMLElement|string)[]=[];
if(container){
const title = this.title.deref();
if(title) title.innerHTML = "";
container.innerHTML = "";
if(this.subOptions){ if(this.subOptions){
container.append(this.subOptions.generateHTML()); //more code needed, though this is enough for now if(this.name!==""){
if(title){
const name = document.createElement("span"); const name = document.createElement("span");
name.innerText = this.name; name.innerText = this.name;
name.classList.add("clickable"); name.classList.add("clickable");
name.onclick = ()=>{ name.onclick = ()=>{
this.returnFromSub(); this.returnFromSub();
}; };
title.append(name, " > ", this.subOptions.name); build.push(name);
build.push(" > ");
}
if(this.subOptions instanceof Options){
build.push(...this.subOptions.generateName());
}else{
build.push(...this.subOptions.options.generateName());
} }
}else{ }else{
const name = document.createElement("span");
name.innerText = this.name;
build.push(name);
}
return build;
}
isTop(){
(this.owner instanceof Form&&this.owner.owner.subOptions!==this.owner),
(this.owner instanceof Settings),
(this.owner instanceof Buttons));
return (this.owner instanceof Options&&this.owner.subOptions!==this)||
(this.owner instanceof Form&&this.owner.owner.subOptions!==this.owner)||
(this.owner instanceof Settings)||
(this.owner instanceof Buttons);
}
generateContainter(){
const container = this.container.deref();
if(container){
const title = this.title.deref();
if(title) title.innerHTML = "";
container.innerHTML = "";
if(this.isTop()){
if(title){
const elms=this.generateName();
title.append(...elms);
}
}
if(!this.subOptions){
for(const thing of this.options){ for(const thing of this.options){
this.generate(thing); this.generate(thing);
} }
if(title){ }else{
title.innerText = this.name; container.append(this.subOptions.generateHTML());
}
} }
if(title && title.innerText !== ""){ if(title && title.innerText !== ""){
title.classList.add("settingstitle"); title.classList.add("settingstitle");
}else if(title){ }else if(title){
title.classList.remove("settingstitle"); title.classList.remove("settingstitle");
} }
if(this.owner instanceof Form&&this.owner.button){
const button=this.owner.button.deref();
if(button){
button.hidden=false;
}
}
}else{ }else{
console.warn("tried to generate container, but it did not exist"); console.warn("tried to generate container, but it did not exist");
} }
@@ -818,7 +864,7 @@ class Form implements OptionsElement<object>{
this.name = name; this.name = name;
this.method = method; this.method = method;
this.submitText = submitText; this.submitText = submitText;
this.options = new Options("", this, { ltr }); this.options = new Options(name, this, { ltr });
this.owner = owner; this.owner = owner;
this.fetchURL = fetchURL; this.fetchURL = fetchURL;
this.headers = headers; this.headers = headers;
@@ -829,6 +875,36 @@ class Form implements OptionsElement<object>{
//the value can't really be anything, but I don't care enough to fix this //the value can't really be anything, but I don't care enough to fix this
this.values[key] = value; this.values[key] = value;
} }
addSubOptions(name: string, { ltr = false } = {}){
if(this.button&&this.button.deref()){
(this.button.deref() as HTMLElement).hidden=true;
}
return this.options.addSubOptions(name,{ltr});
}
addSubForm(
name: string,
onSubmit: (arg1: object) => void,
{
ltr = false,
submitText = "Submit",
fetchURL = "",
headers = {},
method = "POST",
traditionalSubmit = false,
} = {}
){
if(this.button&&this.button.deref()){
console.warn("hidden");
(this.button.deref() as HTMLElement).hidden=true;
}
return this.options.addSubForm(name,onSubmit,{ltr,submitText,fetchURL,headers,method,traditionalSubmit});
}
generateContainter(){
this.options.generateContainter();
if((this.options.isTop())&&this.button&&this.button.deref()){
(this.button.deref() as HTMLElement).hidden=false;
}
}
addSelect( addSelect(
label: string, label: string,
formName: string, formName: string,
@@ -903,7 +979,9 @@ class Form implements OptionsElement<object>{
} }
return mdInput; return mdInput;
} }
addButtonInput(label:string,textContent:string,onSubmit:()=>void){
return this.options.addButtonInput(label,textContent,onSubmit);
}
addCheckboxInput( addCheckboxInput(
label: string, label: string,
formName: string, formName: string,
@@ -917,11 +995,12 @@ class Form implements OptionsElement<object>{
return box; return box;
} }
addText(str: string){ addText(str: string){
this.options.addText(str); return this.options.addText(str);
} }
addTitle(str: string){ addTitle(str: string){
this.options.addTitle(str); this.options.addTitle(str);
} }
button!:WeakRef<HTMLButtonElement>;
generateHTML(): HTMLElement{ generateHTML(): HTMLElement{
const div = document.createElement("div"); const div = document.createElement("div");
div.append(this.options.generateHTML()); div.append(this.options.generateHTML());
@@ -933,6 +1012,10 @@ class Form implements OptionsElement<object>{
}; };
button.textContent = this.submitText; button.textContent = this.submitText;
div.append(button); div.append(button);
if(this.options.subOptions){
button.hidden=true;
}
this.button=new WeakRef(button);
} }
return div; return div;
} }
@@ -1110,4 +1193,4 @@ class Settings extends Buttons{
} }
} }
export{ Settings, OptionsElement, Buttons, Options }; export{ Settings, OptionsElement, Buttons, Options,Form };