get rid of the dialog class/improved notifications

This commit is contained in:
MathMan05 2024-11-27 18:16:01 -06:00
parent 258fba7b8c
commit d4d5da9da4
15 changed files with 500 additions and 785 deletions

View file

@ -2,11 +2,10 @@
import{ Message }from"./message.js"; import{ Message }from"./message.js";
import{ AVoice }from"./audio.js"; import{ AVoice }from"./audio.js";
import{ Contextmenu }from"./contextmenu.js"; import{ Contextmenu }from"./contextmenu.js";
import{ Dialog }from"./dialog.js";
import{ Guild }from"./guild.js"; import{ Guild }from"./guild.js";
import{ Localuser }from"./localuser.js"; import{ Localuser }from"./localuser.js";
import{ Permissions }from"./permissions.js"; import{ Permissions }from"./permissions.js";
import{ Settings }from"./settings.js"; import{ BDialog, Settings }from"./settings.js";
import{ Role, RoleList }from"./role.js"; import{ Role, RoleList }from"./role.js";
import{ InfiniteScroller }from"./infiniteScroller.js"; import{ InfiniteScroller }from"./infiniteScroller.js";
import{ SnowFlake }from"./snowflake.js"; import{ SnowFlake }from"./snowflake.js";
@ -43,7 +42,7 @@ class Channel extends SnowFlake{
lastpin!: string; lastpin!: string;
move_id?: string; move_id?: string;
typing!: number; typing!: number;
message_notifications!: number; message_notifications:number=3;
allthewayup!: boolean; allthewayup!: boolean;
static contextmenu = new Contextmenu<Channel, undefined>("channel menu"); static contextmenu = new Contextmenu<Channel, undefined>("channel menu");
replyingto!: Message | null; replyingto!: Message | null;
@ -53,6 +52,14 @@ class Channel extends SnowFlake{
messages: Map<string, Message> = new Map(); messages: Map<string, Message> = new Map();
voice?:Voice; voice?:Voice;
bitrate:number=128000; bitrate:number=128000;
muted:boolean=false;
mute_config= {selected_time_window: -1,end_time: 0}
handleUserOverrides(settings:{message_notifications: number,muted: boolean,mute_config: {selected_time_window: number,end_time: number},channel_id: string}){
this.message_notifications=settings.message_notifications;
this.muted=settings.muted;
this.mute_config=settings.mute_config;
}
static setupcontextmenu(){ static setupcontextmenu(){
this.contextmenu.addbutton(()=>I18n.getTranslation("channel.copyId"), function(this: Channel){ this.contextmenu.addbutton(()=>I18n.getTranslation("channel.copyId"), function(this: Channel){
navigator.clipboard.writeText(this.id); navigator.clipboard.writeText(this.id);
@ -76,6 +83,12 @@ class Channel extends SnowFlake{
return this.isAdmin(); return this.isAdmin();
} }
); );
this.contextmenu.addbutton(
()=>I18n.getTranslation("guild.notifications"),
function(){
this.setnotifcation();
}
)
this.contextmenu.addbutton( this.contextmenu.addbutton(
()=>I18n.getTranslation("channel.makeInvite"), ()=>I18n.getTranslation("channel.makeInvite"),
@ -129,50 +142,21 @@ class Channel extends SnowFlake{
}); });
}; };
update(); update();
new Dialog([ const inviteOptions=new BDialog("",{noSubmit:true});
"vdiv", inviteOptions.options.addTitle(I18n.getTranslation("inviteOptions.title"));
["title", I18n.getTranslation("inviteOptions.title")], inviteOptions.options.addText(I18n.getTranslation("invite.subtext",this.name,this.guild.properties.name));
["text", `to #${this.name} in ${this.guild.properties.name}`],
[
"select",
"Expire after:",
[
I18n.getTranslation("inviteOptions.30m"),
I18n.getTranslation("inviteOptions.1h"),
I18n.getTranslation("inviteOptions.6h"),
I18n.getTranslation("inviteOptions.12h"),
I18n.getTranslation("inviteOptions.1d"),
I18n.getTranslation("inviteOptions.7d"),
I18n.getTranslation("inviteOptions.30d"),
I18n.getTranslation("inviteOptions.never"),
],
function(e: Event){
expires = [1800, 3600, 21600, 43200, 86400, 604800, 2592000, 0,][(e.srcElement as HTMLSelectElement).selectedIndex];
update(); inviteOptions.options.addSelect(I18n.getTranslation("invite.expireAfter"),()=>{},
}, ["30m","1h","6h","12h","1d","7d","30d","never"].map((e)=>I18n.getTranslation("inviteOptions."+e))
0, ).onchange=(e)=>{expires=[1800, 3600, 21600, 43200, 86400, 604800, 2592000, 0][e];update()};
],
[ const timeOptions=["1","5","10","25","50","100"].map((e)=>I18n.getTranslation("inviteOptions.limit",e))
"select", timeOptions.unshift(I18n.getTranslation("inviteOptions.noLimit"))
"Max uses:", inviteOptions.options.addSelect(I18n.getTranslation("invite.expireAfter"),()=>{},timeOptions)
[ .onchange=(e)=>{uses=[0, 1, 5, 10, 25, 50, 100][e];update()};
I18n.getTranslation("inviteOptions.noLimit"),
I18n.getTranslation("inviteOptions.limit","1"), inviteOptions.options.addHTMLArea(div);
I18n.getTranslation("inviteOptions.limit","5"), inviteOptions.show();
I18n.getTranslation("inviteOptions.limit","10"),
I18n.getTranslation("inviteOptions.limit","25"),
I18n.getTranslation("inviteOptions.limit","50"),
I18n.getTranslation("inviteOptions.limit","100"),
],
function(e: Event){
uses = [0, 1, 5, 10, 25, 50, 100][(e.srcElement as HTMLSelectElement).selectedIndex];
update();
},
0,
],
["html", div],
]).show();
} }
generateSettings(){ generateSettings(){
this.sortPerms(); this.sortPerms();
@ -938,6 +922,81 @@ class Channel extends SnowFlake{
} }
} }
lastmessage: Message | undefined; lastmessage: Message | undefined;
setnotifcation(){
const defualt=I18n.getTranslation("guild."+["all", "onlyMentions", "none","default"][this.guild.message_notifications])
const options=["all", "onlyMentions", "none","default"].map(e=>I18n.getTranslation("guild."+e,defualt));
const notiselect=new BDialog("");
const form=notiselect.options.addForm("",(_,sent:any)=>{
notiselect.hide();
console.log(sent);
this.message_notifications = sent.channel_overrides[this.id].message_notifications;
},{
fetchURL:`${this.info.api}/users/@me/guilds/${this.guild.id}/settings/`,
method:"PATCH",
headers:this.headers
});
form.addSelect(I18n.getTranslation("guild.selectnoti"),"message_notifications",options,{
radio:true,
defaultIndex:this.message_notifications
},[0,1,2,3]);
form.addPreprocessor((e:any)=>{
const message_notifications=e.message_notifications;
delete e.message_notifications;
e.channel_overrides={
[this.id]:{
message_notifications,
muted:this.muted,
mute_config:this.mute_config,
channel_id:this.id
}
}
})
/*
let noti = this.message_notifications;
const defualt=I18n.getTranslation("guild."+["all", "onlyMentions", "none","default"][this.guild.message_notifications])
const options=["all", "onlyMentions", "none","default"].map(e=>I18n.getTranslation("guild."+e,defualt))
const notiselect = new Dialog([
"vdiv",
[
"radio",
I18n.getTranslation("guild.selectnoti"),
options,
function(e: string){
noti = options.indexOf(e);
},
noti,
],
[
"button",
"",
"submit",
(_: any)=>{
//
fetch(this.info.api + `/users/@me/guilds/${this.guild.id}/settings/`, {
method: "PATCH",
headers: this.headers,
body: JSON.stringify({
channel_overrides:{
[this.id]:{
message_notifications: noti,
muted:false,
mute_config:{
selected_time_window:0,
end_time:0
},
channel_id:this.id
}
}
}),
}).then(()=>notiselect.hide());
this.message_notifications = noti;
},
],
]);
*/
notiselect.show();
}
async putmessages(){ async putmessages(){
//TODO swap out with the WS op code //TODO swap out with the WS op code
if(this.allthewayup){ if(this.allthewayup){
@ -1229,16 +1288,17 @@ class Channel extends SnowFlake{
notinumber = null; notinumber = null;
} }
notinumber ??= this.guild.message_notifications; notinumber ??= this.guild.message_notifications;
console.warn("info:",notinumber);
switch(Number(notinumber)){ switch(Number(notinumber)){
case 0: case 0:
return"all"; return"all";
case 1: case 1:
return"mentions"; return"mentions";
case 2: case 2:
return"none"; return"none";
case 3: case 3:
default: default:
return"default"; return"default";
} }
} }
async sendMessage( async sendMessage(

View file

@ -1,273 +0,0 @@
type dialogjson =
| ["hdiv", ...dialogjson[]]
| ["vdiv", ...dialogjson[]]
| ["img", string, [number, number] | undefined | ["fit"]]
| ["checkbox", string, boolean, (this: HTMLInputElement, e: Event) => unknown]
| ["button", string, string, (this: HTMLButtonElement, e: Event) => unknown]
| ["mdbox", string, string, (this: HTMLTextAreaElement, e: Event) => unknown]
| ["textbox", string, string, (this: HTMLInputElement, e: Event) => unknown]
| ["fileupload", string, (this: HTMLInputElement, e: Event) => unknown]
| ["text", string]
| ["title", string]
| ["radio", string, string[], (this: unknown, e: string) => unknown, number]
| ["html", HTMLElement]
| ["select", string, string[], (this: HTMLSelectElement, e: Event) => unknown, number]
| ["tabs", [string, dialogjson][]];
class Dialog{
layout: dialogjson;
onclose: Function;
onopen: Function;
html: HTMLDivElement;
background!: HTMLDivElement;
constructor(
layout: dialogjson,
onclose = (_: any)=>{},
onopen = (_: any)=>{}
){
this.layout = layout;
this.onclose = onclose;
this.onopen = onopen;
const div = document.createElement("div");
div.appendChild(this.tohtml(layout));
this.html = div;
this.html.classList.add("centeritem");
if(!(layout[0] === "img")){
this.html.classList.add("nonimagecenter");
}
}
tohtml(array: dialogjson): HTMLElement{
switch(array[0]){
case"img":
const img = document.createElement("img");
img.src = array[1];
if(array[2] != undefined){
if(array[2].length === 2){
img.width = array[2][0];
img.height = array[2][1];
}else if(array[2][0] === "fit"){
img.classList.add("imgfit");
}
}
return img;
case"hdiv":
const hdiv = document.createElement("div");
hdiv.classList.add("flexltr");
for(const thing of array){
if(thing === "hdiv"){
continue;
}
hdiv.appendChild(this.tohtml(thing));
}
return hdiv;
case"vdiv":
const vdiv = document.createElement("div");
vdiv.classList.add("flexttb");
for(const thing of array){
if(thing === "vdiv"){
continue;
}
vdiv.appendChild(this.tohtml(thing));
}
return vdiv;
case"checkbox": {
const div = document.createElement("div");
const checkbox = document.createElement("input");
div.appendChild(checkbox);
const label = document.createElement("span");
checkbox.checked = array[2];
label.textContent = array[1];
div.appendChild(label);
checkbox.addEventListener("change", array[3]);
checkbox.type = "checkbox";
return div;
}
case"button": {
const div = document.createElement("div");
const input = document.createElement("button");
const label = document.createElement("span");
input.textContent = array[2];
label.textContent = array[1];
div.appendChild(label);
div.appendChild(input);
input.addEventListener("click", array[3]);
return div;
}
case"mdbox": {
const div = document.createElement("div");
const input = document.createElement("textarea");
input.value = array[2];
const label = document.createElement("span");
label.textContent = array[1];
input.addEventListener("input", array[3]);
div.appendChild(label);
div.appendChild(document.createElement("br"));
div.appendChild(input);
return div;
}
case"textbox": {
const div = document.createElement("div");
const input = document.createElement("input");
input.value = array[2];
input.type = "text";
const label = document.createElement("span");
label.textContent = array[1];
console.log(array[3]);
input.addEventListener("input", array[3]);
div.appendChild(label);
div.appendChild(input);
return div;
}
case"fileupload": {
const div = document.createElement("div");
const input = document.createElement("input");
input.type = "file";
const label = document.createElement("span");
label.textContent = array[1];
div.appendChild(label);
div.appendChild(input);
input.addEventListener("change", array[2]);
console.log(array);
return div;
}
case"text": {
const span = document.createElement("span");
span.textContent = array[1];
return span;
}
case"title": {
const span = document.createElement("span");
span.classList.add("title");
span.textContent = array[1];
return span;
}
case"radio": {
const div = document.createElement("div");
const fieldset = document.createElement("fieldset");
fieldset.addEventListener("change", ()=>{
let i = -1;
for(const thing of Array.from(fieldset.children)){
i++;
if(i === 0){
continue;
}
const checkbox = thing.children[0].children[0] as HTMLInputElement;
if(checkbox.checked){
array[3](checkbox.value);
}
}
});
const legend = document.createElement("legend");
legend.textContent = array[1];
fieldset.appendChild(legend);
let i = 0;
for(const thing of array[2]){
const div = document.createElement("div");
const input = document.createElement("input");
input.classList.add("radio");
input.type = "radio";
input.name = array[1];
input.value = thing;
if(i === array[4]){
input.checked = true;
}
const label = document.createElement("label");
label.appendChild(input);
const span = document.createElement("span");
span.textContent = thing;
label.appendChild(span);
div.appendChild(label);
fieldset.appendChild(div);
i++;
}
div.appendChild(fieldset);
return div;
}
case"html":
return array[1];
case"select": {
const div = document.createElement("div");
const label = document.createElement("label");
const selectSpan = document.createElement("span");
selectSpan.classList.add("selectspan");
const select = document.createElement("select");
const selectArrow = document.createElement("span");
selectArrow.classList.add("svgicon","svg-category","selectarrow");
label.textContent = array[1];
selectSpan.append(select);
selectSpan.append(selectArrow);
div.append(label);
div.appendChild(selectSpan);
for(const thing of array[2]){
const option = document.createElement("option");
option.textContent = thing;
select.appendChild(option);
}
select.selectedIndex = array[4];
select.addEventListener("change", array[3]);
return div;
}
case"tabs": {
const table = document.createElement("div");
table.classList.add("flexttb");
const tabs = document.createElement("div");
tabs.classList.add("flexltr");
tabs.classList.add("tabbed-head");
table.appendChild(tabs);
const content = document.createElement("div");
content.classList.add("tabbed-content");
table.appendChild(content);
let shown: HTMLElement | undefined;
for(const thing of array[1]){
const button = document.createElement("button");
button.textContent = thing[0];
tabs.appendChild(button);
const html = this.tohtml(thing[1]);
content.append(html);
if(!shown){
shown = html;
}else{
html.style.display = "none";
}
button.addEventListener("click", _=>{
if(shown){
shown.style.display = "none";
}
html.style.display = "";
shown = html;
});
}
return table;
}
default:
console.error(
"can't find element:" + array[0],
" full element:",
array
);
return document.createElement("span");
}
}
show(){
this.onopen();
console.log("fullscreen");
this.background = document.createElement("div");
this.background.classList.add("background");
document.body.appendChild(this.background);
document.body.appendChild(this.html);
this.background.onclick = _=>{
this.hide();
};
}
hide(){
document.body.removeChild(this.background);
document.body.removeChild(this.html);
}
}
export{ Dialog };

37
src/webpage/disimg.ts Normal file
View file

@ -0,0 +1,37 @@
class ImagesDisplay{
images:string[];
index=0;
constructor(srcs:string[],index=0){
this.images=srcs;
this.index=index;
}
weakbg=new WeakRef<HTMLElement>(document.createElement("div"));
get background():HTMLElement|undefined{
return this.weakbg.deref();
}
set background(e:HTMLElement){
this.weakbg=new WeakRef(e);
}
makeHTML():HTMLElement{
//TODO this should be able to display more than one image at a time lol
const image= document.createElement("img");
image.src=this.images[this.index];
image.classList.add("imgfit","centeritem");
return image;
}
show(){
this.background = document.createElement("div");
this.background.classList.add("background");
this.background.appendChild(this.makeHTML());
this.background.onclick = _=>{
this.hide();
};
document.body.append(this.background);
}
hide(){
if(this.background){
this.background.remove();
}
}
}
export{ImagesDisplay}

View file

@ -1,10 +1,10 @@
import{ Dialog }from"./dialog.js";
import{ Message }from"./message.js"; import{ Message }from"./message.js";
import{ MarkDown }from"./markdown.js"; import{ MarkDown }from"./markdown.js";
import{ embedjson, invitejson }from"./jsontypes.js"; import{ embedjson, invitejson }from"./jsontypes.js";
import{ getapiurls, getInstances }from"./login.js"; import{ getapiurls, getInstances }from"./login.js";
import{ Guild }from"./guild.js"; import{ Guild }from"./guild.js";
import { I18n } from "./i18n.js"; import { I18n } from "./i18n.js";
import { ImagesDisplay } from "./disimg.js";
class Embed{ class Embed{
type: string; type: string;
@ -178,7 +178,7 @@ Url.pathname.split("/")[Url.pathname.split("/").length - 1];
const img = document.createElement("img"); const img = document.createElement("img");
img.classList.add("messageimg"); img.classList.add("messageimg");
img.onclick = function(){ img.onclick = function(){
const full = new Dialog(["img", img.src, ["fit"]]); const full = new ImagesDisplay([img.src]);
full.show(); full.show();
}; };
img.src = this.json.thumbnail.proxy_url; img.src = this.json.thumbnail.proxy_url;
@ -214,7 +214,7 @@ Url.pathname.split("/")[Url.pathname.split("/").length - 1];
if(this.json.thumbnail){ if(this.json.thumbnail){
img.classList.add("embedimg"); img.classList.add("embedimg");
img.onclick = function(){ img.onclick = function(){
const full = new Dialog(["img", img.src, ["fit"]]); const full = new ImagesDisplay([img.src]);
full.show(); full.show();
}; };
img.src = this.json.thumbnail.proxy_url; img.src = this.json.thumbnail.proxy_url;
@ -392,7 +392,7 @@ guild as invitejson["guild"] & { info: { cdn: string } }
}; };
}else{ }else{
img.onclick = async ()=>{ img.onclick = async ()=>{
const full = new Dialog(["img", img.src, ["fit"]]); const full = new ImagesDisplay([img.src]);
full.show(); full.show();
}; };
} }

View file

@ -1,6 +1,6 @@
import{ Message }from"./message.js"; import{ Message }from"./message.js";
import{ Dialog }from"./dialog.js";
import{ filejson }from"./jsontypes.js"; import{ filejson }from"./jsontypes.js";
import { ImagesDisplay } from "./disimg.js";
class File{ class File{
owner: Message | null; owner: Message | null;
@ -40,7 +40,7 @@ class File{
img.classList.add("messageimg"); img.classList.add("messageimg");
div.classList.add("messageimgdiv"); div.classList.add("messageimgdiv");
img.onclick = function(){ img.onclick = function(){
const full = new Dialog(["img", img.src, ["fit"]]); const full = new ImagesDisplay([img.src]);
full.show(); full.show();
}; };
img.src = src; img.src = src;

View file

@ -2,9 +2,8 @@ import{ Channel }from"./channel.js";
import{ Localuser }from"./localuser.js"; import{ Localuser }from"./localuser.js";
import{ Contextmenu }from"./contextmenu.js"; import{ Contextmenu }from"./contextmenu.js";
import{ Role, RoleList }from"./role.js"; import{ Role, RoleList }from"./role.js";
import{ Dialog }from"./dialog.js";
import{ Member }from"./member.js"; import{ Member }from"./member.js";
import{ Settings }from"./settings.js"; import{ BDialog, Settings }from"./settings.js";
import{ Permissions }from"./permissions.js"; import{ Permissions }from"./permissions.js";
import{ SnowFlake }from"./snowflake.js"; import{ SnowFlake }from"./snowflake.js";
import{channeljson,guildjson,emojijson,memberjson,invitejson,rolesjson,}from"./jsontypes.js"; import{channeljson,guildjson,emojijson,memberjson,invitejson,rolesjson,}from"./jsontypes.js";
@ -238,7 +237,7 @@ class Guild extends SnowFlake{
this.localuser.perminfo.guilds[this.id] = e; this.localuser.perminfo.guilds[this.id] = e;
} }
notisetting(settings: { notisetting(settings: {
channel_overrides?: unknown[]; channel_overrides: {message_notifications: number,muted: boolean,mute_config: {selected_time_window: number,end_time: number},channel_id: string}[];
message_notifications: any; message_notifications: any;
flags?: number; flags?: number;
hide_muted_channels?: boolean; hide_muted_channels?: boolean;
@ -253,66 +252,42 @@ class Guild extends SnowFlake{
guild_id?: string; guild_id?: string;
}){ }){
this.message_notifications = settings.message_notifications; this.message_notifications = settings.message_notifications;
for(const override of settings.channel_overrides){
const channel=this.localuser.channelids.get(override.channel_id);
if(!channel) continue;
channel.handleUserOverrides(override);
}
} }
setnotifcation(){ setnotifcation(){
let noti = this.message_notifications;
const options=["all", "onlyMentions", "none"].map(e=>I18n.getTranslation("guild."+e)) const options=["all", "onlyMentions", "none"].map(e=>I18n.getTranslation("guild."+e));
const notiselect = new Dialog([ const notiselect=new BDialog("");
"vdiv", const form=notiselect.options.addForm("",(_,sent:any)=>{
[ notiselect.hide();
"radio", this.message_notifications = sent.message_notifications;
I18n.getTranslation("guild.selectnoti"), },{
options, fetchURL:`${this.info.api}/users/@me/guilds/${this.id}/settings/`,
function(e: string){ method:"PATCH",
noti = options.indexOf(e); headers:this.headers
}, });
noti, form.addSelect(I18n.getTranslation("guild.selectnoti"),"message_notifications",options,{
], radio:true,
[ defaultIndex:this.message_notifications
"button", },[0,1,2]);
"",
"submit",
(_: any)=>{
//
fetch(this.info.api + `/users/@me/guilds/${this.id}/settings/`, {
method: "PATCH",
headers: this.headers,
body: JSON.stringify({
message_notifications: noti,
}),
});
this.message_notifications = noti;
},
],
]);
notiselect.show(); notiselect.show();
} }
confirmleave(){ confirmleave(){
const full = new Dialog([ const full = new BDialog("");
"vdiv", full.options.addTitle(I18n.getTranslation("guild.confirmLeave"))
["title", I18n.getTranslation("guild.confirmLeave")], const options=full.options.addOptions("",{ltr:true});
[ options.addButtonInput("",I18n.getTranslation("guild.yesLeave"),()=>{
"hdiv", this.leave().then(_=>{
[ full.hide();
"button", });
"", });
I18n.getTranslation("guild.yesLeave"), options.addButtonInput("",I18n.getTranslation("guild.noLeave"),()=>{
(_: any)=>{ full.hide();
this.leave().then(_=>{ });
full.hide();
});
},
],
[
"button",
"",
I18n.getTranslation("guild.noLeave"),
(_: any)=>{
full.hide();
},
],
],
]);
full.show(); full.show();
} }
async leave(){ async leave(){
@ -458,46 +433,26 @@ class Guild extends SnowFlake{
} }
confirmDelete(){ confirmDelete(){
let confirmname = ""; let confirmname = "";
const full = new Dialog([
"vdiv", const full = new BDialog("");
[ full.options.addTitle(I18n.getTranslation("guild.confirmDelete",this.properties.name));
"title", full.options.addTextInput(I18n.getTranslation("guild.serverName"),()=>{}).onchange=(e)=>confirmname=e;
I18n.getTranslation("guild.confirmDelete",this.properties.name)
], const options=full.options.addOptions("",{ltr:true});
[ options.addButtonInput("",I18n.getTranslation("guild.yesDelete"),()=>{
"textbox", if(confirmname !== this.properties.name){
I18n.getTranslation("guild.serverName"), //TODO maybe some sort of form error? idk
"", alert("names don't match");
function(this: HTMLInputElement){ return;
confirmname = this.value; }
}, this.delete().then(_=>{
], full.hide();
[ });
"hdiv", });
[
"button", options.addButtonInput("",I18n.getTranslation("guild.noDelete"),()=>{
"", full.hide();
I18n.getTranslation("guild.yesDelete"), });
(_: any)=>{
console.log(confirmname);
if(confirmname !== this.properties.name){
return;
}
this.delete().then(_=>{
full.hide();
});
},
],
[
"button",
"",
I18n.getTranslation("guild.noDelete"),
(_: any)=>{
full.hide();
},
],
],
]);
full.show(); full.show();
} }
async delete(){ async delete(){
@ -677,67 +632,27 @@ class Guild extends SnowFlake{
return thischannel; return thischannel;
} }
createchannels(func = this.createChannel){ createchannels(func = this.createChannel){
let name = ""; const options=["text", "announcement","voice"].map(e=>I18n.getTranslation("channel."+e));
let category = 0;
const options=["voice", "text", "announcement"].map(e=>I18n.getTranslation("channel."+e)); const channelselect=new BDialog("");
const numbers=[2,0,5] const form=channelselect.options.addForm("",(e:any)=>{
const channelselect = new Dialog([ func(e.name,e.type);
"vdiv", channelselect.hide();
[ });
"radio",
I18n.getTranslation("channel.selectType"), form.addSelect(I18n.getTranslation("channel.selectType"),"type",options,{radio:true},[0,5,2]);
options, form.addTextInput(I18n.getTranslation("channel.selectName"),"name");
function(radio: string){
console.log(radio);
category = numbers[options.indexOf(radio)] || 0;
},
1,
],
[
"textbox",
I18n.getTranslation("channel.selectName"),
"",
function(this: HTMLInputElement){
name = this.value;
},
],
[
"button",
"",
I18n.getTranslation("submit"),
()=>{
console.log(name, category);
func.bind(this)(name, category);
channelselect.hide();
},
],
]);
channelselect.show(); channelselect.show();
} }
createcategory(){ createcategory(){
let name = "";
const category = 4; const category = 4;
const channelselect = new Dialog([ const channelselect=new BDialog("");
"vdiv", const options=channelselect.options;
[ const form=options.addForm("",(e:any)=>{
"textbox", this.createChannel(e.name, category);
I18n.getTranslation("channel.selectCatName"), channelselect.hide();
"", });
function(this: HTMLInputElement){ form.addTextInput(I18n.getTranslation("channel.selectCatName"),"name");
name = this.value;
},
],
[
"button",
"",
I18n.getTranslation("submit"),
function(this:Guild){
console.log(name, category);
this.createChannel(name, category);
channelselect.hide();
}.bind(this),
],
]);
channelselect.show(); channelselect.show();
} }
delChannel(json: channeljson){ delChannel(json: channeljson){

View file

@ -63,7 +63,7 @@ type readyjson = {
}; };
user_guild_settings: { user_guild_settings: {
entries: { entries: {
channel_overrides: unknown[]; //will have to find example channel_overrides: {message_notifications: number,muted: boolean,mute_config: {selected_time_window: number,end_time: number},channel_id: string}[];
message_notifications: number; message_notifications: number;
flags: number; flags: number;
hide_muted_channels: boolean; hide_muted_channels: boolean;
@ -523,6 +523,11 @@ roleCreate | {
nickname: null nickname: null
}, },
s: number s: number
}|{
op: 0,
t: "PRESENCE_UPDATE",
d: presencejson,
s:number
}; };

View file

@ -3,11 +3,10 @@ import{ Channel }from"./channel.js";
import{ Direct }from"./direct.js"; import{ Direct }from"./direct.js";
import{ AVoice }from"./audio.js"; import{ AVoice }from"./audio.js";
import{ User }from"./user.js"; import{ User }from"./user.js";
import{ Dialog }from"./dialog.js";
import{ getapiurls, getBulkInfo, setTheme, Specialuser, SW }from"./login.js"; import{ getapiurls, getBulkInfo, setTheme, Specialuser, SW }from"./login.js";
import{channeljson,guildjson,mainuserjson,memberjson,memberlistupdatejson,messageCreateJson,presencejson,readyjson,startTypingjson,wsjson,}from"./jsontypes.js"; import{channeljson,guildjson,mainuserjson,memberjson,memberlistupdatejson,messageCreateJson,presencejson,readyjson,startTypingjson,wsjson,}from"./jsontypes.js";
import{ Member }from"./member.js"; import{ Member }from"./member.js";
import{ Form, FormError, Options, Settings }from"./settings.js"; import{ BDialog, Form, FormError, Options, Settings }from"./settings.js";
import{ getTextNodeAtPosition, MarkDown }from"./markdown.js"; import{ getTextNodeAtPosition, MarkDown }from"./markdown.js";
import { Bot } from "./bot.js"; import { Bot } from "./bot.js";
import { Role } from "./role.js"; import { Role } from "./role.js";
@ -26,8 +25,6 @@ class Localuser{
initialized!: boolean; initialized!: boolean;
info!: Specialuser["serverurls"]; info!: Specialuser["serverurls"];
headers!: { "Content-type": string; Authorization: string }; headers!: { "Content-type": string; Authorization: string };
userConnections!: Dialog;
devPortal!: Dialog;
ready!: readyjson; ready!: readyjson;
guilds!: Guild[]; guilds!: Guild[];
guildids: Map<string, Guild> = new Map(); guildids: Map<string, Guild> = new Map();
@ -559,6 +556,12 @@ class Localuser{
this.relationshipsUpdate(); this.relationshipsUpdate();
break; break;
} }
case "PRESENCE_UPDATE":{
if(temp.d.user){
this.presences.set(temp.d.user.id, temp.d);
}
break;
}
default :{ default :{
//@ts-ignore //@ts-ignore
console.warn("Unhandled case "+temp.t,temp); console.warn("Unhandled case "+temp.t,temp);
@ -888,99 +891,44 @@ class Localuser{
this.unreads(); this.unreads();
} }
createGuild(){ createGuild(){
let inviteurl = "";
const error = document.createElement("span"); const full=new BDialog("");
const fields: { name: string; icon: string | null } = { const buttons=full.options.addButtons("",{top:true});
name: "", const viacode=buttons.add(I18n.getTranslation("invite.joinUsing"));
icon: null, {
}; const form=viacode.addForm("",async (e: any)=>{
const full = new Dialog([ let parsed = "";
"tabs", if(e.code.includes("/")){
[ parsed = e.code.split("/")[e.code.split("/").length - 1];
[ }else{
I18n.getTranslation("invite.joinUsing"), parsed = e.code;
[ }
"vdiv", const json=await (await fetch(this.info.api + "/invites/" + parsed, {
[ method: "POST",
"textbox", headers: this.headers,
I18n.getTranslation("invite.inviteLinkCode"), })).json()
"", if(json.message){
function(this: HTMLInputElement){ throw new FormError(text,json.message);
inviteurl = this.value; }
}, full.hide();
], });
["html", error], const text=form.addTextInput(I18n.getTranslation("invite.inviteLinkCode"),"code");
[ }
"button", const guildcreate=buttons.add(I18n.getTranslation("guild.create"));
"", {
I18n.getTranslation("submit"), const form=guildcreate.addForm("",(fields:any)=>{
(_: any)=>{ this.makeGuild(fields).then(_=>{
let parsed = ""; if(_.message){
if(inviteurl.includes("/")){ alert(_.errors.name._errors[0].message);
parsed = }else{
inviteurl.split("/")[inviteurl.split("/").length - 1]; full.hide();
}else{ }
parsed = inviteurl; });
} });
fetch(this.info.api + "/invites/" + parsed, { form.addFileInput(I18n.getTranslation("guild.icon:"),"icon",{files:"one"});
method: "POST", form.addTextInput(I18n.getTranslation("guild.name:"),"name",{required:true});
headers: this.headers,
}) }
.then(r=>r.json())
.then(_=>{
if(_.message){
error.textContent = _.message;
}
});
},
],
],
],
[
I18n.getTranslation("guild.create"),
[
"vdiv",
["title", I18n.getTranslation("guild.create")],
[
"fileupload",
I18n.getTranslation("guild.icon:"),
function(event: Event){
const target = event.target as HTMLInputElement;
if(!target.files)return;
const reader = new FileReader();
reader.readAsDataURL(target.files[0]);
reader.onload = ()=>{
fields.icon = reader.result as string;
};
},
],
[
"textbox",
I18n.getTranslation("guild.name:"),
"",
function(this: HTMLInputElement, event: Event){
const target = event.target as HTMLInputElement;
fields.name = target.value;
},
],
[
"button",
"",
I18n.getTranslation("submit"),
()=>{
this.makeGuild(fields).then(_=>{
if(_.message){
alert(_.errors.name._errors[0].message);
}else{
full.hide();
}
});
},
],
],
],
],
]);
full.show(); full.show();
} }
async makeGuild(fields: { name: string; icon: string | null }){ async makeGuild(fields: { name: string; icon: string | null }){
@ -996,7 +944,8 @@ class Localuser{
const content = document.createElement("div"); const content = document.createElement("div");
content.classList.add("flexttb","guildy"); content.classList.add("flexttb","guildy");
content.textContent = I18n.getTranslation("guild.loadingDiscovery"); content.textContent = I18n.getTranslation("guild.loadingDiscovery");
const full = new Dialog(["html", content]); const full = new BDialog("");
full.options.addHTMLArea(content);
full.show(); full.show();
const res = await fetch(this.info.api + "/discoverable-guilds?limit=50", { const res = await fetch(this.info.api + "/discoverable-guilds?limit=50", {
@ -2147,15 +2096,12 @@ class Localuser{
headers: this.headers, headers: this.headers,
}); });
const json = await res.json(); const json = await res.json();
const dialog = new BDialog("");
const dialog = new Dialog([ dialog.options.addTitle(I18n.getTranslation("instanceStats.name",this.instancePing.name));
"vdiv", dialog.options.addText(I18n.getTranslation("instanceStats.users",json.counts.user));
["title", I18n.getTranslation("instanceStats.name",this.instancePing.name) ], dialog.options.addText(I18n.getTranslation("instanceStats.servers",json.counts.guild));
["text", I18n.getTranslation("instanceStats.users",json.counts.user)], dialog.options.addText(I18n.getTranslation("instanceStats.messages",json.counts.message));
["text", I18n.getTranslation("instanceStats.servers",json.counts.guild)], dialog.options.addText(I18n.getTranslation("instanceStats.members",json.counts.members));
["text", I18n.getTranslation("instanceStats.messages",json.counts.message)],
["text", I18n.getTranslation("instanceStats.members",json.counts.members)],
]);
dialog.show(); dialog.show();
} }
} }

View file

@ -1,5 +1,5 @@
import{ Dialog }from"./dialog.js";
import { I18n } from "./i18n.js"; import { I18n } from "./i18n.js";
import { BDialog, FormError } from "./settings.js";
const mobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); const mobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
const iOS = /iPhone|iPad|iPod/i.test(navigator.userAgent); const iOS = /iPhone|iPad|iPod/i.test(navigator.userAgent);
@ -511,65 +511,43 @@ async function login(username: string, password: string, captcha: string){
capty.setAttribute("data-sitekey", response.captcha_sitekey); capty.setAttribute("data-sitekey", response.captcha_sitekey);
const script = document.createElement("script"); const script = document.createElement("script");
script.src = "https://js.hcaptcha.com/1/api.js"; script.src = "https://js.hcaptcha.com/1/api.js";
capt!.append(script); capt!.append(script);
capt!.append(capty); capt!.append(capty);
} }
}else{ }else{
console.log(response); console.log(response);
if(response.ticket){ if(response.ticket){
let onetimecode = ""; const better=new BDialog("");
new Dialog([ const form=better.options.addForm("",(res:any)=>{
"vdiv", if(res.message){
["title", I18n.getTranslation("2faCode")], throw new FormError(ti,res.message);
[ }else{
"textbox", console.warn(res);
"", if(!res.token)return;
"", adduser({
function(this: HTMLInputElement){ serverurls: JSON.parse(localStorage.getItem("instanceinfo") as string),
// eslint-disable-next-line no-invalid-this email: username,
onetimecode = this.value; token: res.token,
}, }).username = username;
], const redir = new URLSearchParams(
[ window.location.search
"button", ).get("goback");
"", if(redir){
I18n.getTranslation("submit"), window.location.href = redir;
function(){ }else{
fetch(api + "/auth/mfa/totp", { window.location.href = "/channels/@me";
method: "POST", }
headers: { }
"Content-Type": "application/json", },{
}, fetchURL:api + "/auth/mfa/totp",
body: JSON.stringify({ method:"POST",
code: onetimecode, headers:{
ticket: response.ticket, "Content-Type": "application/json",
}), }
}) });
.then(r=>r.json()) form.addTitle(I18n.getTranslation("2faCode"));
.then(res=>{ const ti=form.addTextInput("","code");
if(res.message){ better.show()
alert(res.message);
}else{
console.warn(res);
if(!res.token)return;
adduser({
serverurls: JSON.parse(localStorage.getItem("instanceinfo") as string),
email: username,
token: res.token,
}).username = username;
const redir = new URLSearchParams(
window.location.search
).get("goback");
if(redir){
window.location.href = redir;
}else{
window.location.href = "/channels/@me";
}
}
});
},
],
]).show();
}else{ }else{
console.warn(response); console.warn(response);
if(!response.token)return; if(!response.token)return;

View file

@ -1,10 +1,10 @@
import{ Channel }from"./channel.js"; import{ Channel }from"./channel.js";
import{ Dialog }from"./dialog.js";
import{ Emoji }from"./emoji.js"; import{ Emoji }from"./emoji.js";
import{ Guild }from"./guild.js"; import{ Guild }from"./guild.js";
import { I18n } from "./i18n.js"; import { I18n } from "./i18n.js";
import{ Localuser }from"./localuser.js"; import{ Localuser }from"./localuser.js";
import{ Member }from"./member.js"; import{ Member }from"./member.js";
import { BDialog } from "./settings.js";
class MarkDown{ class MarkDown{
txt: string[]; txt: string[];
@ -781,37 +781,20 @@ txt[j + 1] === undefined)
if(this.trustedDomains.has(Url.host)){ if(this.trustedDomains.has(Url.host)){
open(); open();
}else{ }else{
const full: Dialog = new Dialog([ const full=new BDialog("");
"vdiv", full.options.addTitle(I18n.getTranslation("leaving"));
["title", I18n.getTranslation("leaving")], full.options.addText(I18n.getTranslation("goingToURL",Url.host));
[ const options=full.options.addOptions("",{ltr:true});
"text", options.addButtonInput("",I18n.getTranslation("nevermind"),()=>full.hide());
I18n.getTranslation("goingToURL",Url.host) options.addButtonInput("",I18n.getTranslation("goThere"),()=>{
], open();
[ full.hide();
"hdiv", });
["button", "", I18n.getTranslation("nevermind"), (_: any)=>full.hide()], options.addButtonInput("",I18n.getTranslation("goThereTrust"),()=>{
[ open();
"button", full.hide();
"", this.trustedDomains.add(Url.host);
I18n.getTranslation("goThere"), });
(_: any)=>{
open();
full.hide();
},
],
[
"button",
"",
I18n.getTranslation("goThereTrust"),
(_: any)=>{
open();
full.hide();
this.trustedDomains.add(Url.host);
},
],
],
]);
full.show(); full.show();
} }
}; };

View file

@ -3,8 +3,8 @@ import{ Role }from"./role.js";
import{ Guild }from"./guild.js"; import{ Guild }from"./guild.js";
import{ SnowFlake }from"./snowflake.js"; import{ SnowFlake }from"./snowflake.js";
import{ memberjson, presencejson }from"./jsontypes.js"; import{ memberjson, presencejson }from"./jsontypes.js";
import{ Dialog }from"./dialog.js";
import { I18n } from "./i18n.js"; import { I18n } from "./i18n.js";
import { BDialog } from "./settings.js";
class Member extends SnowFlake{ class Member extends SnowFlake{
static already = {}; static already = {};
@ -229,28 +229,13 @@ class Member extends SnowFlake{
return this.nick || this.user.username; return this.nick || this.user.username;
} }
kick(){ kick(){
let reason = ""; const menu = new BDialog("");
const menu = new Dialog([ const form=menu.options.addForm("",((e:any)=>{
"vdiv", this.kickAPI(e.reason);
["title", I18n.getTranslation("member.kick",this.name,this.guild.properties.name)], menu.hide();
[ }));
"textbox", form.addTitle(I18n.getTranslation("member.kick",this.name,this.guild.properties.name));
I18n.getTranslation("member.reason:"), form.addTextInput(I18n.getTranslation("member.reason:"),"reason");
"",
function(e: Event){
reason = (e.target as HTMLInputElement).value;
},
],
[
"button",
"",
I18n.getTranslation("submit"),
()=>{
this.kickAPI(reason);
menu.hide();
},
],
]);
menu.show(); menu.show();
} }
kickAPI(reason: string){ kickAPI(reason: string){
@ -262,28 +247,13 @@ class Member extends SnowFlake{
}); });
} }
ban(){ ban(){
let reason = ""; const menu = new BDialog("");
const menu = new Dialog([ const form=menu.options.addForm("",((e:any)=>{
"vdiv", this.banAPI(e.reason);
["title", I18n.getTranslation("member.ban",this.name,this.guild.properties.name)], menu.hide();
[ }));
"textbox", form.addTitle(I18n.getTranslation("member.ban",this.name,this.guild.properties.name));
I18n.getTranslation("member.reason:",this.name,this.guild.properties.name), form.addTextInput(I18n.getTranslation("member.reason:"),"reason");
"",
function(e: Event){
reason = (e.target as HTMLInputElement).value;
},
],
[
"button",
"",
I18n.getTranslation("submit",this.name,this.guild.properties.name),
()=>{
this.banAPI(reason);
menu.hide();
},
],
]);
menu.show(); menu.show();
} }
addRole(role:Role){ addRole(role:Role){

View file

@ -10,10 +10,10 @@ import{ File }from"./file.js";
import{ SnowFlake }from"./snowflake.js"; import{ SnowFlake }from"./snowflake.js";
import{ memberjson, messagejson }from"./jsontypes.js"; import{ memberjson, messagejson }from"./jsontypes.js";
import{ Emoji }from"./emoji.js"; import{ Emoji }from"./emoji.js";
import{ Dialog }from"./dialog.js";
import{ mobile }from"./login.js"; import{ mobile }from"./login.js";
import { I18n } from "./i18n.js"; import { I18n } from "./i18n.js";
import { Hover } from "./hover.js"; import { Hover } from "./hover.js";
import { BDialog } from "./settings.js";
class Message extends SnowFlake{ class Message extends SnowFlake{
static contextmenu = new Contextmenu<Message, undefined>("message menu"); static contextmenu = new Contextmenu<Message, undefined>("message menu");
@ -87,7 +87,7 @@ class Message extends SnowFlake{
Message.contextmenu.addbutton( Message.contextmenu.addbutton(
()=>I18n.getTranslation("message.delete"), ()=>I18n.getTranslation("message.delete"),
function(this: Message){ function(this: Message){
this.delete(); this.confirmDelete();
}, },
null, null,
function(){ function(){
@ -674,31 +674,7 @@ class Message extends SnowFlake{
this.delete(); this.delete();
return; return;
} }
const diaolog = new Dialog([ this.confirmDelete();
"vdiv",
["title", I18n.getTranslation("deleteConfirm")],
[
"hdiv",
[
"button",
"",
I18n.getTranslation("yes"),
()=>{
this.delete();
diaolog.hide();
},
],
[
"button",
"",
I18n.getTranslation("no"),
()=>{
diaolog.hide();
},
],
]
]);
diaolog.show();
}; };
} }
if(buttons.childNodes.length !== 0){ if(buttons.childNodes.length !== 0){
@ -714,6 +690,19 @@ class Message extends SnowFlake{
}; };
} }
} }
confirmDelete(){
const diaolog=new BDialog("");
diaolog.options.addTitle(I18n.getTranslation("deleteConfirm"));
const options=diaolog.options.addOptions("",{ltr:true});
options.addButtonInput("",I18n.getTranslation("yes"),()=>{
this.delete();
diaolog.hide();
});
options.addButtonInput("",I18n.getTranslation("no"),()=>{
diaolog.hide();
})
diaolog.show();
}
updateReactions(){ updateReactions(){
const reactdiv = this.reactdiv.deref(); const reactdiv = this.reactdiv.deref();
if(!reactdiv)return; if(!reactdiv)return;

View file

@ -14,7 +14,9 @@ class Buttons implements OptionsElement<unknown>{
buttonList!: HTMLDivElement; buttonList!: HTMLDivElement;
warndiv!: HTMLElement; warndiv!: HTMLElement;
value: unknown; value: unknown;
constructor(name: string){ top=false;
constructor(name: string,{top=false}={}){
this.top=top;
this.buttons = []; this.buttons = [];
this.name = name; this.name = name;
} }
@ -28,7 +30,7 @@ class Buttons implements OptionsElement<unknown>{
generateHTML(){ generateHTML(){
const buttonList = document.createElement("div"); const buttonList = document.createElement("div");
buttonList.classList.add("Buttons"); buttonList.classList.add("Buttons");
buttonList.classList.add("flexltr"); buttonList.classList.add(this.top?"flexttb":"flexltr");
this.buttonList = buttonList; this.buttonList = buttonList;
const htmlarea = document.createElement("div"); const htmlarea = document.createElement("div");
htmlarea.classList.add("flexgrow"); htmlarea.classList.add("flexgrow");
@ -43,6 +45,9 @@ class Buttons implements OptionsElement<unknown>{
generateButtons(optionsArea:HTMLElement){ generateButtons(optionsArea:HTMLElement){
const buttonTable = document.createElement("div"); const buttonTable = document.createElement("div");
buttonTable.classList.add("settingbuttons"); buttonTable.classList.add("settingbuttons");
if(this.top){
buttonTable.classList.add("flexltr");
}
for(const thing of this.buttons){ for(const thing of this.buttons){
const button = document.createElement("button"); const button = document.createElement("button");
button.classList.add("SettingsButton"); button.classList.add("SettingsButton");
@ -328,6 +333,7 @@ class SelectInput implements OptionsElement<number>{
options: string[]; options: string[];
index: number; index: number;
select!: WeakRef<HTMLSelectElement>; select!: WeakRef<HTMLSelectElement>;
radio:boolean;
get value(){ get value(){
return this.index; return this.index;
} }
@ -336,15 +342,61 @@ class SelectInput implements OptionsElement<number>{
onSubmit: (str: number) => void, onSubmit: (str: number) => void,
options: string[], options: string[],
owner: Options, owner: Options,
{ defaultIndex = 0 } = {} { defaultIndex = 0,radio=false } = {}
){ ){
this.label = label; this.label = label;
this.index = defaultIndex; this.index = defaultIndex;
this.owner = owner; this.owner = owner;
this.onSubmit = onSubmit; this.onSubmit = onSubmit;
this.options = options; this.options = options;
this.radio=radio;
} }
generateHTML(): HTMLDivElement{ generateHTML(): HTMLDivElement{
if(this.radio){
const map=new WeakMap<HTMLInputElement,number>();
const div = document.createElement("div");
const fieldset = document.createElement("fieldset");
fieldset.addEventListener("change", ()=>{
let i = -1;
for(const thing of Array.from(fieldset.children)){
i++;
if(i === 0){
continue;
}
const checkbox = thing.children[0].children[0] as HTMLInputElement;
if(checkbox.checked){
this.onChange(map.get(checkbox));
}
}
});
const legend = document.createElement("legend");
legend.textContent = this.label;
fieldset.appendChild(legend);
let i = 0;
for(const thing of this.options){
const div = document.createElement("div");
const input = document.createElement("input");
input.classList.add("radio");
input.type = "radio";
input.name = this.label;
input.value = thing;
map.set(input,i);
if(i === this.index){
input.checked = true;
}
const label = document.createElement("label");
label.appendChild(input);
const span = document.createElement("span");
span.textContent = thing;
label.appendChild(span);
div.appendChild(label);
fieldset.appendChild(div);
i++;
}
div.appendChild(fieldset);
return div;
}
const div = document.createElement("div"); const div = document.createElement("div");
const span = document.createElement("span"); const span = document.createElement("span");
span.textContent = this.label; span.textContent = this.label;
@ -353,7 +405,7 @@ class SelectInput implements OptionsElement<number>{
selectSpan.classList.add("selectspan"); selectSpan.classList.add("selectspan");
const select = document.createElement("select"); const select = document.createElement("select");
select.onchange = this.onChange.bind(this); select.onchange = this.onChange.bind(this,-1);
for(const thing of this.options){ for(const thing of this.options){
const option = document.createElement("option"); const option = document.createElement("option");
option.textContent = thing; option.textContent = thing;
@ -368,8 +420,13 @@ class SelectInput implements OptionsElement<number>{
div.append(selectSpan); div.append(selectSpan);
return div; return div;
} }
private onChange(){ private onChange(index=-1){
this.owner.changed(); this.owner.changed();
if(index!==-1){
this.onchange(index);
this.index = index;
return;
}
const select = this.select.deref(); const select = this.select.deref();
if(select){ if(select){
const value = select.selectedIndex; const value = select.selectedIndex;
@ -524,7 +581,7 @@ class Float{
/** /**
* This is a simple wrapper class for Options to make it happy so it can be used outside of Settings. * This is a simple wrapper class for Options to make it happy so it can be used outside of Settings.
*/ */
constructor(name:string, options={ ltr:false, noSubmit:false}){ constructor(name:string, options={ ltr:false, noSubmit:true}){
this.options=new Options(name,this,options) this.options=new Options(name,this,options)
} }
changed=()=>{}; changed=()=>{};
@ -532,6 +589,38 @@ class Float{
return this.options.generateHTML(); return this.options.generateHTML();
} }
} }
class BDialog{
float:Float;
get options(){
return this.float.options;
}
background=new WeakRef(document.createElement("div"));
constructor(name:string, { ltr=false, noSubmit=true}={}){
this.float=new Float(name,{ltr,noSubmit});
}
show(){
const background = document.createElement("div");
background.classList.add("background");
const center=this.float.generateHTML();
center.classList.add("centeritem","nonimagecenter");
center.classList.remove("titlediv");
background.append(center);
center.onclick=e=>{
e.stopImmediatePropagation();
}
document.body.append(background);
this.background=new WeakRef(background);
background.onclick = _=>{
background.remove();
};
}
hide(){
const background=this.background.deref();
if(!background) return;
background.remove();
}
}
export{BDialog};
class Options implements OptionsElement<void>{ class Options implements OptionsElement<void>{
name: string; name: string;
haschanged = false; haschanged = false;
@ -571,6 +660,12 @@ class Options implements OptionsElement<void>{
this.generate(options); this.generate(options);
return options; return options;
} }
addButtons(name: string, { top = false } = {}){
const buttons = new Buttons(name, { top });
this.options.push(buttons);
this.generate(buttons);
return buttons;
}
subOptions: Options | Form | undefined; subOptions: Options | Form | undefined;
genTop(){ genTop(){
const container = this.container.deref(); const container = this.container.deref();
@ -596,7 +691,7 @@ class Options implements OptionsElement<void>{
} }
addSubForm( addSubForm(
name: string, name: string,
onSubmit: (arg1: object) => void, onSubmit: (arg1: object,sent:object) => void,
{ {
ltr = false, ltr = false,
submitText = "Submit", submitText = "Submit",
@ -626,10 +721,10 @@ class Options implements OptionsElement<void>{
label: string, label: string,
onSubmit: (str: number) => void, onSubmit: (str: number) => void,
selections: string[], selections: string[],
{ defaultIndex = 0 } = {} { defaultIndex = 0,radio=false } = {}
){ ){
const select = new SelectInput(label, onSubmit, selections, this, { const select = new SelectInput(label, onSubmit, selections, this, {
defaultIndex, defaultIndex,radio
}); });
this.options.push(select); this.options.push(select);
this.generate(select); this.generate(select);
@ -717,7 +812,7 @@ class Options implements OptionsElement<void>{
} }
addForm( addForm(
name: string, name: string,
onSubmit: (arg1: object) => void, onSubmit: (arg1: object,sent:object) => void,
{ {
ltr = false, ltr = false,
submitText = "Submit", submitText = "Submit",
@ -901,7 +996,7 @@ class Form implements OptionsElement<object>{
constructor( constructor(
name: string, name: string,
owner: Options, owner: Options,
onSubmit: (arg1: object) => void, onSubmit: (arg1: object,sent:object) => void,
{ {
ltr = false, ltr = false,
submitText = I18n.getTranslation("submit"), submitText = I18n.getTranslation("submit"),
@ -934,7 +1029,7 @@ class Form implements OptionsElement<object>{
} }
addSubForm( addSubForm(
name: string, name: string,
onSubmit: (arg1: object) => void, onSubmit: (arg1: object,sent:object) => void,
{ {
ltr = false, ltr = false,
submitText = I18n.getTranslation("submit"), submitText = I18n.getTranslation("submit"),
@ -956,16 +1051,16 @@ class Form implements OptionsElement<object>{
(this.button.deref() as HTMLElement).hidden=false; (this.button.deref() as HTMLElement).hidden=false;
} }
} }
selectMap=new WeakMap<SelectInput,string[]>(); selectMap=new WeakMap<SelectInput,(number|string)[]>();
addSelect( addSelect(
label: string, label: string,
formName: string, formName: string,
selections: string[], selections: string[],
{ defaultIndex = 0, required = false}={}, { defaultIndex = 0, required = false,radio=false}={},
correct:string[]=selections correct:(string|number)[]=selections
){ ){
const select = this.options.addSelect(label, _=>{}, selections, { const select = this.options.addSelect(label, _=>{}, selections, {
defaultIndex, defaultIndex,radio
}); });
this.selectMap.set(select,correct); this.selectMap.set(select,correct);
this.names.set(formName, select); this.names.set(formName, select);
@ -1084,7 +1179,7 @@ class Form implements OptionsElement<object>{
} }
return div; return div;
} }
onSubmit: (arg1: object) => void; onSubmit: ((arg1: object,sent:object) => void )|((arg1: object,sent:object) => Promise<void> );
watchForChange(func: (arg1: object) => void){ watchForChange(func: (arg1: object) => void){
this.onSubmit = func; this.onSubmit = func;
} }
@ -1187,14 +1282,14 @@ class Form implements OptionsElement<object>{
if(_==="") return {}; if(_==="") return {};
return JSON.parse(_) return JSON.parse(_)
}) })
.then(json=>{ .then(async json=>{
if(json.errors){ if(json.errors){
if(this.errors(json)){ if(this.errors(json)){
return; return;
} }
} }
try{ try{
this.onSubmit(json); await this.onSubmit(json,build);
}catch(e){ }catch(e){
console.error(e); console.error(e);
if(e instanceof FormError){ if(e instanceof FormError){
@ -1211,7 +1306,7 @@ class Form implements OptionsElement<object>{
}); });
}else{ }else{
try{ try{
this.onSubmit(build); await this.onSubmit(build,build);
}catch(e){ }catch(e){
if(e instanceof FormError){ if(e instanceof FormError){
const elm = this.options.html.get(e.elem); const elm = this.options.html.get(e.elem);

View file

@ -1614,12 +1614,12 @@ img.bigembedimg {
gap: 8px; gap: 8px;
overflow-y: auto; overflow-y: auto;
} }
.nonimagecenter .flexttb, .nonimagecenter .flexltr { .nonimagecenter & .flexttb, .nonimagecenter & .flexltr {
flex: 1; flex: 1;
gap: 8px; gap: 8px;
} }
.nonimagecenter > .flexttb, .nonimagecenter > .flexltr { .nonimagecenter > .flexttb, .nonimagecenter > .flexltr {
padding: 16px; padding: 16px !important;
background: var(--primary-bg); background: var(--primary-bg);
border-radius: 8px; border-radius: 8px;
} }
@ -1758,6 +1758,12 @@ fieldset input[type="radio"] {
.Buttons { .Buttons {
flex: 1; flex: 1;
} }
.settingbuttons.flexltr{
width: 100%;
.SettingsButton{
width:auto;
}
}
.settingbuttons { .settingbuttons {
flex: none; flex: none;
width: 192px; width: 192px;
@ -1800,7 +1806,8 @@ fieldset input[type="radio"] {
} }
.optionElement, .FormSettings > button { .optionElement, .FormSettings > button {
margin: 16px 16px 0 16px; margin: 16px 16px 0 16px;
word-break: break-word; /* word-break: break-word; */
overflow: hidden;
} }
.optionElement:has(.optionElement) { .optionElement:has(.optionElement) {
margin: 0; margin: 0;
@ -1915,7 +1922,7 @@ fieldset input[type="radio"] {
height: calc(100svh - 50px); height: calc(100svh - 50px);
} }
.flexspace { .flexspace {
flex-direction: column; /*flex-direction: column;*/
} }
.optionElement input[type="text"], .optionElement input[type="text"],
.optionElement textarea, .optionElement textarea,

View file

@ -152,7 +152,7 @@
"nsfw:":"NSFW:", "nsfw:":"NSFW:",
"selectType":"Select channel type", "selectType":"Select channel type",
"selectName":"Name of channel", "selectName":"Name of channel",
"selectCatName":"Name of channel", "selectCatName":"Name of category",
"createChannel":"Create channel", "createChannel":"Create channel",
"createCatagory":"Create category" "createCatagory":"Create category"
}, },
@ -219,7 +219,7 @@
"selectnoti":"Select notifications type", "selectnoti":"Select notifications type",
"all":"all", "all":"all",
"onlyMentions":"only mentions", "onlyMentions":"only mentions",
"none":"node", "none":"none",
"confirmLeave":"Are you sure you want to leave?", "confirmLeave":"Are you sure you want to leave?",
"yesLeave":"Yes, I'm sure", "yesLeave":"Yes, I'm sure",
"noLeave":"Nevermind", "noLeave":"Nevermind",
@ -231,7 +231,8 @@
"loadingDiscovery":"Loading...", "loadingDiscovery":"Loading...",
"disoveryTitle":"Guild discovery ($1) {{PLURAL:$1|entry|entries}}", "disoveryTitle":"Guild discovery ($1) {{PLURAL:$1|entry|entries}}",
"emptytitle":"Weird spot", "emptytitle":"Weird spot",
"emptytext":"You're in a weird spot, this guild has no channels" "emptytext":"You're in a weird spot, this guild has no channels",
"default":"Default ($1)"
}, },
"role":{ "role":{
"displaySettings":"Display settings", "displaySettings":"Display settings",
@ -345,7 +346,9 @@
"longInvitedBy":"$1 invited you to join $2", "longInvitedBy":"$1 invited you to join $2",
"loginOrCreateAccount":"Login or create an account ⇌", "loginOrCreateAccount":"Login or create an account ⇌",
"joinUsing":"Join using invite", "joinUsing":"Join using invite",
"inviteLinkCode":"Invite Link/Code" "inviteLinkCode":"Invite Link/Code",
"subtext":"to $1 in $2",
"expireAfter":"Expire after:"
}, },
"friends":{ "friends":{
"blocked":"Blocked", "blocked":"Blocked",