push current translation support progress

This commit is contained in:
MathMan05 2024-10-31 22:58:37 -05:00
parent 602b16a0ef
commit a0d870c1b3
14 changed files with 243 additions and 195 deletions

View file

@ -6,6 +6,7 @@ import { User } from "./user.js";
import {guildjson} from "./jsontypes.js"; import {guildjson} from "./jsontypes.js";
import { PermissionToggle } from "./role.js"; import { PermissionToggle } from "./role.js";
import { Permissions } from "./permissions.js"; import { Permissions } from "./permissions.js";
import { I18n } from "./i18n.js";
class Bot{ class Bot{
readonly owner:Localuser; readonly owner:Localuser;
readonly token:string; readonly token:string;
@ -27,7 +28,7 @@ class Bot{
}; };
} }
settings(){ settings(){
const settings = new Settings("Bot Settings"); const settings = new Settings(I18n.getTranslation("botSettings"));
const botOptions = settings.addButton("Profile",{ltr:true}); const botOptions = settings.addButton("Profile",{ltr:true});
const bot=new User(this.json,this.localuser); const bot=new User(this.json,this.localuser);
{ {
@ -50,7 +51,7 @@ class Bot{
settingsRight.addHTMLArea(hypotheticalProfile); settingsRight.addHTMLArea(hypotheticalProfile);
const finput = settingsLeft.addFileInput( const finput = settingsLeft.addFileInput(
"Upload pfp:", I18n.getTranslation("uploadPfp"),
_=>{ _=>{
if(file){ if(file){
this.updatepfp(file); this.updatepfp(file);
@ -76,7 +77,7 @@ class Bot{
}); });
let bfile: undefined | File | null; let bfile: undefined | File | null;
const binput = settingsLeft.addFileInput( const binput = settingsLeft.addFileInput(
"Upload banner:", I18n.getTranslation("uploadBanner"),
_=>{ _=>{
if(bfile !== undefined){ if(bfile !== undefined){
this.updatebanner(bfile); this.updatebanner(bfile);
@ -102,7 +103,7 @@ class Bot{
}); });
let changed = false; let changed = false;
const pronounbox = settingsLeft.addTextInput( const pronounbox = settingsLeft.addTextInput(
"Pronouns", I18n.getTranslation("pronouns"),
_=>{ _=>{
if(newpronouns || newbio || changed){ if(newpronouns || newbio || changed){
this.updateProfile({ this.updateProfile({
@ -119,7 +120,7 @@ class Bot{
newpronouns = _; newpronouns = _;
regen(); regen();
}); });
const bioBox = settingsLeft.addMDInput("Bio:", _=>{}, { const bioBox = settingsLeft.addMDInput(I18n.getTranslation("bio"), _=>{}, {
initText: bot.bio.rawString, initText: bot.bio.rawString,
}); });
bioBox.watchForChange(_=>{ bioBox.watchForChange(_=>{
@ -134,7 +135,7 @@ class Bot{
color = "transparent"; color = "transparent";
} }
const colorPicker = settingsLeft.addColorInput( const colorPicker = settingsLeft.addColorInput(
"Profile color", I18n.getTranslation("profileColor"),
_=>{}, _=>{},
{ initColor: color } { initColor: color }
); );
@ -148,7 +149,7 @@ class Bot{
} }
{ {
const guildsettings=settings.addButton("Guilds"); const guildsettings=settings.addButton("Guilds");
guildsettings.addTitle("Guilds bot is in:"); guildsettings.addTitle(I18n.getTranslation("botGuilds"));
fetch(this.info.api+"/users/@me/guilds/",{ fetch(this.info.api+"/users/@me/guilds/",{
headers:this.headers headers:this.headers
}).then(_=>_.json()).then((json:(guildjson["properties"])[])=>{ }).then(_=>_.json()).then((json:(guildjson["properties"])[])=>{
@ -187,8 +188,8 @@ class Bot{
content.onclick=()=>{ content.onclick=()=>{
const guildsetting=guildsettings.addSubOptions(guild.name); const guildsetting=guildsettings.addSubOptions(guild.name);
guildsetting.addHTMLArea(content); guildsetting.addHTMLArea(content);
guildsetting.addButtonInput("","Leave Guild",()=>{ guildsetting.addButtonInput("",I18n.getTranslation("leaveGuild"),()=>{
if(confirm(`Are you sure you want to leave ${guild.name}?`)){ if(confirm(I18n.getTranslation("confirmGuildLeave",guild.name))){
fetch(this.info.api+"/users/@me/guilds/"+guild.id,{ fetch(this.info.api+"/users/@me/guilds/"+guild.id,{
method:"DELETE", method:"DELETE",
headers:this.headers headers:this.headers
@ -250,7 +251,7 @@ class Bot{
}); });
} }
static InviteMaker(id:string,container:Form,info:Localuser["info"]){ static InviteMaker(id:string,container:Form,info:Localuser["info"]){
const gen=container.addSubOptions("URL generator",{ const gen=container.addSubOptions(I18n.getTranslation("UrlGen"),{
noSubmit:true noSubmit:true
}); });
const params = new URLSearchParams(""); const params = new URLSearchParams("");

View file

@ -15,6 +15,7 @@ import{ MarkDown }from"./markdown.js";
import{ Member }from"./member.js"; import{ Member }from"./member.js";
import { Voice } from "./voice.js"; import { Voice } from "./voice.js";
import { User } from "./user.js"; import { User } from "./user.js";
import { I18n } from "./i18n.js";
declare global { declare global {
interface NotificationOptions { interface NotificationOptions {
@ -53,20 +54,20 @@ class Channel extends SnowFlake{
voice?:Voice; voice?:Voice;
bitrate:number=128000; bitrate:number=128000;
static setupcontextmenu(){ static setupcontextmenu(){
this.contextmenu.addbutton("Copy channel id", function(this: Channel){ this.contextmenu.addbutton(()=>I18n.getTranslation("channel.copyId"), function(this: Channel){
navigator.clipboard.writeText(this.id); navigator.clipboard.writeText(this.id);
}); });
this.contextmenu.addbutton("Mark as read", function(this: Channel){ this.contextmenu.addbutton(()=>I18n.getTranslation("channel.markRead"), function(this: Channel){
this.readbottom(); this.readbottom();
}); });
this.contextmenu.addbutton("Settings", function(this: Channel){ this.contextmenu.addbutton(()=>I18n.getTranslation("channel.settings"), function(this: Channel){
this.generateSettings(); this.generateSettings();
}); });
this.contextmenu.addbutton( this.contextmenu.addbutton(
"Delete channel", ()=>I18n.getTranslation("channel.delete"),
function(this: Channel){ function(this: Channel){
this.deleteChannel(); this.deleteChannel();
}, },
@ -77,7 +78,7 @@ class Channel extends SnowFlake{
); );
this.contextmenu.addbutton( this.contextmenu.addbutton(
"Make invite", ()=>I18n.getTranslation("channel.makeInvite"),
function(this: Channel){ function(this: Channel){
this.createInvite(); this.createInvite();
}, },
@ -86,24 +87,6 @@ class Channel extends SnowFlake{
return this.hasPermission("CREATE_INSTANT_INVITE") && this.type !== 4; return this.hasPermission("CREATE_INSTANT_INVITE") && this.type !== 4;
} }
); );
/*
this.contextmenu.addbutton("Test button",function(){
this.localuser.ws.send(JSON.stringify({
"op": 14,
"d": {
"guild_id": this.guild.id,
"channels": {
[this.id]: [
[
0,
99
]
]
}
}
}))
},null);
/**/
} }
createInvite(){ createInvite(){
const div = document.createElement("div"); const div = document.createElement("div");
@ -148,20 +131,20 @@ class Channel extends SnowFlake{
update(); update();
new Dialog([ new Dialog([
"vdiv", "vdiv",
["title", "Invite people"], ["title", I18n.getTranslation("inviteOptions.title")],
["text", `to #${this.name} in ${this.guild.properties.name}`], ["text", `to #${this.name} in ${this.guild.properties.name}`],
[ [
"select", "select",
"Expire after:", "Expire after:",
[ [
"30 Minutes", I18n.getTranslation("inviteOptions.30m"),
"1 Hour", I18n.getTranslation("inviteOptions.1h"),
"6 Hours", I18n.getTranslation("inviteOptions.6h"),
"12 Hours", I18n.getTranslation("inviteOptions.12h"),
"1 Day", I18n.getTranslation("inviteOptions.1d"),
"7 Days", I18n.getTranslation("inviteOptions.7d"),
"30 Days", I18n.getTranslation("inviteOptions.30d"),
"Never", I18n.getTranslation("inviteOptions.never"),
], ],
function(e: Event){ function(e: Event){
expires = [1800, 3600, 21600, 43200, 86400, 604800, 2592000, 0,][(e.srcElement as HTMLSelectElement).selectedIndex]; expires = [1800, 3600, 21600, 43200, 86400, 604800, 2592000, 0,][(e.srcElement as HTMLSelectElement).selectedIndex];
@ -174,13 +157,13 @@ class Channel extends SnowFlake{
"select", "select",
"Max uses:", "Max uses:",
[ [
"No limit", I18n.getTranslation("inviteOptions.noLimit"),
"1 use", I18n.getTranslation("inviteOptions.limit","1"),
"5 uses", I18n.getTranslation("inviteOptions.limit","5"),
"10 uses", I18n.getTranslation("inviteOptions.limit","10"),
"25 uses", I18n.getTranslation("inviteOptions.limit","25"),
"50 uses", I18n.getTranslation("inviteOptions.limit","50"),
"100 uses", I18n.getTranslation("inviteOptions.limit","100"),
], ],
function(e: Event){ function(e: Event){
uses = [0, 1, 5, 10, 25, 50, 100][(e.srcElement as HTMLSelectElement).selectedIndex]; uses = [0, 1, 5, 10, 25, 50, 100][(e.srcElement as HTMLSelectElement).selectedIndex];
@ -193,7 +176,7 @@ class Channel extends SnowFlake{
} }
generateSettings(){ generateSettings(){
this.sortPerms(); this.sortPerms();
const settings = new Settings("Settings for " + this.name); const settings = new Settings(I18n.getTranslation("channel.settingsFor",this.name));
{ {
const gensettings=settings.addButton("Settings"); const gensettings=settings.addButton("Settings");
const form=gensettings.addForm("",()=>{},{ const form=gensettings.addForm("",()=>{},{
@ -201,18 +184,19 @@ class Channel extends SnowFlake{
method: "PATCH", method: "PATCH",
headers: this.headers, headers: this.headers,
}); });
form.addTextInput("Name:","name",{initText:this.name}); form.addTextInput(I18n.getTranslation("channel.name:"),"name",{initText:this.name});
form.addMDInput("Topic:","topic",{initText:this.topic}); form.addMDInput(I18n.getTranslation("channel.topic:"),"topic",{initText:this.topic});
form.addCheckboxInput("NSFW:","nsfw",{initState:this.nsfw}); form.addCheckboxInput(I18n.getTranslation("channel.nsfw:"),"nsfw",{initState:this.nsfw});
if(this.type!==4){ if(this.type!==4){
const options=["voice", "text", "announcement"]; const options=["voice", "text", "announcement"];
form.addSelect("Type:","type",options,{ form.addSelect("Type:","type",options.map(e=>I18n.getTranslation("channel."+e)),{
defaultIndex:options.indexOf({0:"text", 2:"voice", 5:"announcement", 4:"category" }[this.type] as string) defaultIndex:options.indexOf({0:"text", 2:"voice", 5:"announcement", 4:"category" }[this.type] as string)
},options);
form.addPreprocessor((obj:any)=>{
obj.type={text: 0, voice: 2, announcement: 5, category: 4 }[obj.type as string]
}) })
} }
form.addPreprocessor((obj:any)=>{
obj.type={text: 0, voice: 2, announcement: 5, category: 4 }[obj.type as string]
})
} }
const s1 = settings.addButton("Permissions"); const s1 = settings.addButton("Permissions");
s1.options.push( s1.options.push(
@ -308,10 +292,7 @@ class Channel extends SnowFlake{
this.permission_overwrites = new Map(); this.permission_overwrites = new Map();
this.permission_overwritesar = []; this.permission_overwritesar = [];
for(const thing of json.permission_overwrites){ for(const thing of json.permission_overwrites){
if( if(thing.id === "1182819038095799904" ||thing.id === "1182820803700625444"){
thing.id === "1182819038095799904" ||
thing.id === "1182820803700625444"
){
continue; continue;
} }
if(!this.permission_overwrites.has(thing.id)){ if(!this.permission_overwrites.has(thing.id)){
@ -376,10 +357,10 @@ class Channel extends SnowFlake{
} }
return( return(
Boolean(this.lastmessageid) && Boolean(this.lastmessageid) &&
(!this.lastreadmessageid || (!this.lastreadmessageid ||
SnowFlake.stringToUnixTime(this.lastmessageid as string) > SnowFlake.stringToUnixTime(this.lastmessageid as string) >
SnowFlake.stringToUnixTime(this.lastreadmessageid)) && SnowFlake.stringToUnixTime(this.lastreadmessageid)) &&
this.type !== 4 this.type !== 4
); );
} }
hasPermission(name: string, member = this.guild.member): boolean{ hasPermission(name: string, member = this.guild.member): boolean{
@ -406,10 +387,7 @@ class Channel extends SnowFlake{
return false; return false;
} }
get canMessage(): boolean{ get canMessage(): boolean{
if( if(this.permission_overwritesar.length === 0 &&this.hasPermission("MANAGE_CHANNELS")){
this.permission_overwritesar.length === 0 &&
this.hasPermission("MANAGE_CHANNELS")
){
const role = this.guild.roles.find(_=>_.name === "@everyone"); const role = this.guild.roles.find(_=>_.name === "@everyone");
if(role){ if(role){
this.addRoleToPerms(role); this.addRoleToPerms(role);
@ -435,16 +413,17 @@ class Channel extends SnowFlake{
calculateReorder(){ calculateReorder(){
let position = -1; let position = -1;
const build: { const build: {
id: string; id: string;
position: number | undefined; position: number | undefined;
parent_id: string | undefined; parent_id: string | undefined;
}[] = []; }[] = [];
for(const thing of this.children){ for(const thing of this.children){
const thisthing: { const thisthing: {
id: string; id: string;
position: number | undefined; position: number | undefined;
parent_id: string | undefined; parent_id: string | undefined;
} = { id: thing.id, position: undefined, parent_id: undefined }; } = { id: thing.id, position: undefined, parent_id: undefined };
if(thing.position < position){ if(thing.position < position){
thing.position = thisthing.position = position + 1; thing.position = thisthing.position = position + 1;
} }
@ -652,12 +631,7 @@ class Channel extends SnowFlake{
return; return;
} }
fetch( fetch(
this.info.api + this.info.api +"/channels/" + this.id + "/messages/" + this.lastmessageid + "/ack",
"/channels/" +
this.id +
"/messages/" +
this.lastmessageid +
"/ack",
{ {
method: "POST", method: "POST",
headers: this.headers, headers: this.headers,
@ -768,7 +742,7 @@ class Channel extends SnowFlake{
if(this.replyingto){ if(this.replyingto){
replybox.innerHTML = ""; replybox.innerHTML = "";
const span = document.createElement("span"); const span = document.createElement("span");
span.textContent = "Replying to " + this.replyingto.author.username; span.textContent = I18n.getTranslation("replyingTo", this.replyingto.author.username);
const X = document.createElement("button"); const X = document.createElement("button");
X.onclick = _=>{ X.onclick = _=>{
if(this.replyingto?.div){ if(this.replyingto?.div){
@ -827,9 +801,7 @@ class Channel extends SnowFlake{
history.pushState(null, "", "/channels/" + this.guild_id + "/" + this.id); history.pushState(null, "", "/channels/" + this.guild_id + "/" + this.id);
this.localuser.pageTitle("#" + this.name); this.localuser.pageTitle("#" + this.name);
const channelTopic = document.getElementById( const channelTopic = document.getElementById("channelTopic") as HTMLSpanElement;
"channelTopic"
) as HTMLSpanElement;
if(this.topic){ if(this.topic){
channelTopic.innerHTML = new MarkDown( channelTopic.innerHTML = new MarkDown(
this.topic, this.topic,
@ -855,8 +827,7 @@ class Channel extends SnowFlake{
await this.buildmessages(); await this.buildmessages();
//loading.classList.remove("loading"); //loading.classList.remove("loading");
(document.getElementById("typebox") as HTMLDivElement).contentEditable = (document.getElementById("typebox") as HTMLDivElement).contentEditable =""+this.canMessage;
"" + this.canMessage;
} }
typingmap: Map<Member, number> = new Map(); typingmap: Map<Member, number> = new Map();
async typingStart(typing: startTypingjson): Promise<void>{ async typingStart(typing: startTypingjson): Promise<void>{
@ -893,11 +864,7 @@ class Channel extends SnowFlake{
this.typingmap.delete(thing); this.typingmap.delete(thing);
} }
} }
if(i > 1){ build=I18n.getTranslation("typing",i+"",build);
build += " are typing";
}else{
build += " is typing";
}
if(this.localuser.channelfocus === this){ if(this.localuser.channelfocus === this){
if(showing){ if(showing){
typingtext.classList.remove("hidden"); typingtext.classList.remove("hidden");
@ -1013,12 +980,7 @@ class Channel extends SnowFlake{
} }
await fetch( await fetch(
this.info.api + this.info.api + "/channels/" + this.id +"/messages?before=" + id + "&limit=100",
"/channels/" +
this.id +
"/messages?before=" +
id +
"&limit=100",
{ {
headers: this.headers, headers: this.headers,
} }
@ -1059,10 +1021,10 @@ class Channel extends SnowFlake{
}); });
} }
/** /**
* Please dont use this, its not implemented. * Please dont use this, its not implemented.
* @deprecated * @deprecated
* @todo * @todo
**/ **/
async grabArround(/* id: string */){ async grabArround(/* id: string */){
//currently unused and no plans to use it yet //currently unused and no plans to use it yet
throw new Error("please don't call this, no one has implemented it :P"); throw new Error("please don't call this, no one has implemented it :P");
@ -1099,8 +1061,7 @@ class Channel extends SnowFlake{
if(!removetitle){ if(!removetitle){
const title = document.createElement("h2"); const title = document.createElement("h2");
title.id = "removetitle"; title.id = "removetitle";
title.textContent = title.textContent = I18n.getTranslation("noMessages");
"No messages appear to be here, be the first to say something!";
title.classList.add("titlespace"); title.classList.add("titlespace");
messages.append(title); messages.append(title);
} }
@ -1353,7 +1314,7 @@ class Channel extends SnowFlake{
noticontent ||= message.embeds[0]?.json.title; noticontent ||= message.embeds[0]?.json.title;
noticontent ||= message.content.textContent; noticontent ||= message.content.textContent;
} }
noticontent ||= "Blank Message"; noticontent ||= I18n.getTranslation("blankMessage");
let imgurl: null | string = null; let imgurl: null | string = null;
const images = message.getimages(); const images = message.getimages();
if(images.length){ if(images.length){

View file

@ -12,6 +12,7 @@ import{
import{ Permissions }from"./permissions.js"; import{ Permissions }from"./permissions.js";
import{ SnowFlake }from"./snowflake.js"; import{ SnowFlake }from"./snowflake.js";
import{ Contextmenu }from"./contextmenu.js"; import{ Contextmenu }from"./contextmenu.js";
import { I18n } from "./i18n.js";
class Direct extends Guild{ class Direct extends Guild{
declare channelids: { [key: string]: Group }; declare channelids: { [key: string]: Group };
@ -99,24 +100,24 @@ dmPermissions.setPermission("SPEAK", 1);
dmPermissions.setPermission("STREAM", 1); dmPermissions.setPermission("STREAM", 1);
dmPermissions.setPermission("USE_VAD", 1); dmPermissions.setPermission("USE_VAD", 1);
// @ts-ignore // @ts-ignore I need to look into this lol
class Group extends Channel{ class Group extends Channel{
user: User; user: User;
static contextmenu = new Contextmenu<Group, undefined>("channel menu"); static contextmenu = new Contextmenu<Group, undefined>("channel menu");
static setupcontextmenu(){ static setupcontextmenu(){
this.contextmenu.addbutton("Copy DM id", function(this: Group){ this.contextmenu.addbutton(()=>I18n.getTranslation("DMs.copyId"), function(this: Group){
navigator.clipboard.writeText(this.id); navigator.clipboard.writeText(this.id);
}); });
this.contextmenu.addbutton("Mark as read", function(this: Group){ this.contextmenu.addbutton(()=>I18n.getTranslation("DMs.markRead"), function(this: Group){
this.readbottom(); this.readbottom();
}); });
this.contextmenu.addbutton("Close DM", function(this: Group){ this.contextmenu.addbutton(()=>I18n.getTranslation("DMs.close"), function(this: Group){
this.deleteChannel(); this.deleteChannel();
}); });
this.contextmenu.addbutton("Copy user ID", function(){ this.contextmenu.addbutton(()=>I18n.getTranslation("user.copyId"), function(){
navigator.clipboard.writeText(this.user.id); navigator.clipboard.writeText(this.user.id);
}); });
} }
@ -179,10 +180,7 @@ class Group extends Channel{
const prom = this.infinite.delete(); const prom = this.infinite.delete();
history.pushState(null, "", "/channels/" + this.guild_id + "/" + this.id); history.pushState(null, "", "/channels/" + this.guild_id + "/" + this.id);
this.localuser.pageTitle("@" + this.name); this.localuser.pageTitle("@" + this.name);
(document.getElementById("channelTopic") as HTMLElement).setAttribute( (document.getElementById("channelTopic") as HTMLElement).setAttribute("hidden","");
"hidden",
""
);
const loading = document.getElementById("loadingdiv") as HTMLDivElement; const loading = document.getElementById("loadingdiv") as HTMLDivElement;
Channel.regenLoadingMessages(); Channel.regenLoadingMessages();
@ -306,4 +304,5 @@ class Group extends Channel{
} }
} }
export{ Direct, Group }; export{ Direct, Group };
Group.setupcontextmenu();
Group.setupcontextmenu()

View file

@ -4,6 +4,7 @@ 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";
class Embed{ class Embed{
type: string; type: string;
@ -296,8 +297,7 @@ guild as invitejson["guild"] & { info: { cdn: string } }
guildinfo.append(name); guildinfo.append(name);
const members = document.createElement("span"); const members = document.createElement("span");
members.innerText = members.innerText = "#" + json.channel.name + " • Members: " + guild.member_count;
"#" + json.channel.name + " • Members: " + guild.member_count;
guildinfo.append(members); guildinfo.append(members);
members.classList.add("subtext"); members.classList.add("subtext");
iconrow.append(guildinfo); iconrow.append(guildinfo);
@ -305,12 +305,12 @@ guild as invitejson["guild"] & { info: { cdn: string } }
div.append(iconrow); div.append(iconrow);
const h2 = document.createElement("h2"); const h2 = document.createElement("h2");
h2.textContent = `You've been invited by ${json.inviter.username}`; h2.textContent = I18n.getTranslation("invite.invitedBy",json.inviter.username);
div.append(h2); div.append(h2);
const button = document.createElement("button"); const button = document.createElement("button");
button.textContent = "Accept"; button.textContent = I18n.getTranslation("invite.accept");
if(this.localuser.info.api.startsWith(info.api) && this.localuser.guildids.has(guild.id)){ if(this.localuser.info.api.startsWith(info.api) && this.localuser.guildids.has(guild.id)){
button.textContent = "Already joined"; button.textContent = I18n.getTranslation("invite.alreadyJoined");
button.disabled = true; button.disabled = true;
} }
button.classList.add("acceptinvbutton"); button.classList.add("acceptinvbutton");

View file

@ -2,6 +2,7 @@ import{ Contextmenu }from"./contextmenu.js";
import{ Guild }from"./guild.js"; import{ Guild }from"./guild.js";
import{ Localuser }from"./localuser.js"; import{ Localuser }from"./localuser.js";
//I need to recompile the emoji format for translation
class Emoji{ class Emoji{
static emojis: { static emojis: {
name: string; name: string;
@ -158,13 +159,7 @@ class Emoji{
const img = document.createElement("img"); const img = document.createElement("img");
img.classList.add("pfp", "servericon", "emoji-server"); img.classList.add("pfp", "servericon", "emoji-server");
img.crossOrigin = "anonymous"; img.crossOrigin = "anonymous";
img.src = img.src = localuser.info.cdn+"/icons/"+guild.properties.id+"/"+guild.properties.icon+".png?size=48";
localuser.info.cdn +
"/icons/" +
guild.properties.id +
"/" +
guild.properties.icon +
".png?size=48";
img.alt = "Server: " + guild.properties.name; img.alt = "Server: " + guild.properties.name;
select.appendChild(img); select.appendChild(img);
}else{ }else{

View file

@ -145,9 +145,7 @@ class File{
static filesizehuman(fsize: number){ static filesizehuman(fsize: number){
const i = fsize == 0 ? 0 : Math.floor(Math.log(fsize) / Math.log(1024)); const i = fsize == 0 ? 0 : Math.floor(Math.log(fsize) / Math.log(1024));
return( return(
Number((fsize / Math.pow(1024, i)).toFixed(2)) * 1 + Number((fsize / Math.pow(1024, i)).toFixed(2)) * 1 + " " + ["Bytes", "Kilobytes", "Megabytes", "Gigabytes", "Terabytes"][i] // I don't think this changes across languages, correct me if I'm wrong
" " +
["Bytes", "Kilobytes", "Megabytes", "Gigabytes", "Terabytes"][i]
); );
} }
} }

View file

@ -16,6 +16,7 @@ import{
rolesjson, rolesjson,
}from"./jsontypes.js"; }from"./jsontypes.js";
import{ User }from"./user.js"; import{ User }from"./user.js";
import { I18n } from "./i18n.js";
class Guild extends SnowFlake{ class Guild extends SnowFlake{
owner!: Localuser; owner!: Localuser;
@ -38,20 +39,20 @@ class Guild extends SnowFlake{
members=new Set<Member>(); members=new Set<Member>();
static contextmenu = new Contextmenu<Guild, undefined>("guild menu"); static contextmenu = new Contextmenu<Guild, undefined>("guild menu");
static setupcontextmenu(){ static setupcontextmenu(){
Guild.contextmenu.addbutton("Copy Guild id", function(this: Guild){ Guild.contextmenu.addbutton(()=>I18n.getTranslation("guild.copyId"), function(this: Guild){
navigator.clipboard.writeText(this.id); navigator.clipboard.writeText(this.id);
}); });
Guild.contextmenu.addbutton("Mark as read", function(this: Guild){ Guild.contextmenu.addbutton(()=>I18n.getTranslation("guild.markRead"), function(this: Guild){
this.markAsRead(); this.markAsRead();
}); });
Guild.contextmenu.addbutton("Notifications", function(this: Guild){ Guild.contextmenu.addbutton(()=>I18n.getTranslation("guild.notifications"), function(this: Guild){
this.setnotifcation(); this.setnotifcation();
}); });
Guild.contextmenu.addbutton( Guild.contextmenu.addbutton(
"Leave guild", ()=>I18n.getTranslation("guild.leave"),
function(this: Guild){ function(this: Guild){
this.confirmleave(); this.confirmleave();
}, },
@ -62,7 +63,7 @@ class Guild extends SnowFlake{
); );
Guild.contextmenu.addbutton( Guild.contextmenu.addbutton(
"Delete guild", ()=>I18n.getTranslation("guild.delete"),
function(this: Guild){ function(this: Guild){
this.confirmDelete(); this.confirmDelete();
}, },
@ -73,13 +74,13 @@ class Guild extends SnowFlake{
); );
Guild.contextmenu.addbutton( Guild.contextmenu.addbutton(
"Create invite", ()=>I18n.getTranslation("guild.makeInvite"),
function(this: Guild){}, function(this: Guild){},
null, null,
_=>true, _=>true,
_=>false _=>false
); );
Guild.contextmenu.addbutton("Settings", function(this: Guild){ Guild.contextmenu.addbutton(()=>I18n.getTranslation("guild.settings"), function(this: Guild){
this.generateSettings(); this.generateSettings();
}); });
/* -----things left for later----- /* -----things left for later-----
@ -94,28 +95,28 @@ class Guild extends SnowFlake{
*/ */
} }
generateSettings(){ generateSettings(){
const settings = new Settings("Settings for " + this.properties.name); const settings = new Settings(I18n.getTranslation("guild.settingsFor",this.properties.name));
{ {
const overview = settings.addButton("Overview"); const overview = settings.addButton(I18n.getTranslation("guild.overview"));
const form = overview.addForm("", _=>{}, { const form = overview.addForm("", _=>{}, {
headers: this.headers, headers: this.headers,
traditionalSubmit: true, traditionalSubmit: true,
fetchURL: this.info.api + "/guilds/" + this.id, fetchURL: this.info.api + "/guilds/" + this.id,
method: "PATCH", method: "PATCH",
}); });
form.addTextInput("Name:", "name", { initText: this.properties.name }); form.addTextInput(I18n.getTranslation("guild.name:"), "name", { initText: this.properties.name });
form.addMDInput("Description:", "description", { form.addMDInput("Description:", "description", {
initText: this.properties.description, initText: this.properties.description,
}); });
form.addFileInput("Banner:", "banner", { clear: true }); form.addFileInput(I18n.getTranslation("guild.banner:"), "banner", { clear: true });
form.addFileInput("Icon:", "icon", { clear: true }); form.addFileInput(I18n.getTranslation("guild.icon:"), "icon", { clear: true });
let region = this.properties.region; let region = this.properties.region;
if(!region){ if(!region){
region = ""; region = "";
} }
form.addTextInput("Region:", "region", { initText: region }); form.addTextInput(I18n.getTranslation("guild.region:"), "region", { initText: region });
} }
const s1 = settings.addButton("Roles"); const s1 = settings.addButton(I18n.getTranslation("guild.roles"));
const permlist: [Role, Permissions][] = []; const permlist: [Role, Permissions][] = [];
for(const thing of this.roles){ for(const thing of this.roles){
permlist.push([thing, thing.permissions]); permlist.push([thing, thing.permissions]);
@ -261,14 +262,15 @@ class Guild extends SnowFlake{
} }
setnotifcation(){ setnotifcation(){
let noti = this.message_notifications; let noti = this.message_notifications;
const options=["all", "onlyMentions", "none"].map(e=>I18n.getTranslation("guild."+e))
const notiselect = new Dialog([ const notiselect = new Dialog([
"vdiv", "vdiv",
[ [
"radio", "radio",
"select notifications type", I18n.getTranslation("guild.selectnoti"),
["all", "only mentions", "none"], options,
function(e: string /* "all" | "only mentions" | "none" */){ function(e: string){
noti = ["all", "only mentions", "none"].indexOf(e); noti = options.indexOf(e);
}, },
noti, noti,
], ],
@ -294,13 +296,13 @@ class Guild extends SnowFlake{
confirmleave(){ confirmleave(){
const full = new Dialog([ const full = new Dialog([
"vdiv", "vdiv",
["title", "Are you sure you want to leave?"], ["title", I18n.getTranslation("guild.confirmLeave")],
[ [
"hdiv", "hdiv",
[ [
"button", "button",
"", "",
"Yes, I'm sure", I18n.getTranslation("yesLeave"),
(_: any)=>{ (_: any)=>{
this.leave().then(_=>{ this.leave().then(_=>{
full.hide(); full.hide();
@ -310,7 +312,7 @@ class Guild extends SnowFlake{
[ [
"button", "button",
"", "",
"Nevermind", I18n.getTranslation("noLeave"),
(_: any)=>{ (_: any)=>{
full.hide(); full.hide();
}, },
@ -467,11 +469,11 @@ class Guild extends SnowFlake{
"vdiv", "vdiv",
[ [
"title", "title",
"Are you sure you want to delete " + this.properties.name + "?", I18n.getTranslation("guild.confirmDelete",this.properties.name)
], ],
[ [
"textbox", "textbox",
"Name of server:", I18n.getTranslation("serverName"),
"", "",
function(this: HTMLInputElement){ function(this: HTMLInputElement){
confirmname = this.value; confirmname = this.value;
@ -482,7 +484,7 @@ class Guild extends SnowFlake{
[ [
"button", "button",
"", "",
"Yes, I'm sure", I18n.getTranslation("yesDelete"),
(_: any)=>{ (_: any)=>{
console.log(confirmname); console.log(confirmname);
if(confirmname !== this.properties.name){ if(confirmname !== this.properties.name){
@ -496,7 +498,7 @@ class Guild extends SnowFlake{
[ [
"button", "button",
"", "",
"Nevermind", I18n.getTranslation("noDelete"),
(_: any)=>{ (_: any)=>{
full.hide(); full.hide();
}, },
@ -638,22 +640,23 @@ class Guild extends SnowFlake{
createchannels(func = this.createChannel){ createchannels(func = this.createChannel){
let name = ""; let name = "";
let category = 0; let category = 0;
const options=["voice", "text", "announcement"].map(e=>I18n.getTranslation("channel."+e));
const numbers=[2,0,5]
const channelselect = new Dialog([ const channelselect = new Dialog([
"vdiv", "vdiv",
[ [
"radio", "radio",
"select channel type", I18n.getTranslation("channel.selectType"),
["voice", "text", "announcement"], options,
function(radio: string){ function(radio: string){
console.log(radio); console.log(radio);
category = category = numbers[options.indexOf(radio)] || 0;
{ text: 0, voice: 2, announcement: 5, category: 4 }[radio] || 0;
}, },
1, 1,
], ],
[ [
"textbox", "textbox",
"Name of channel", I18n.getTranslation("channel.selectName"),
"", "",
function(this: HTMLInputElement){ function(this: HTMLInputElement){
name = this.value; name = this.value;
@ -662,7 +665,7 @@ class Guild extends SnowFlake{
[ [
"button", "button",
"", "",
"submit", I18n.getTranslation("submit"),
()=>{ ()=>{
console.log(name, category); console.log(name, category);
func.bind(this)(name, category); func.bind(this)(name, category);
@ -679,7 +682,7 @@ class Guild extends SnowFlake{
"vdiv", "vdiv",
[ [
"textbox", "textbox",
"Name of category", I18n.getTranslation("channel.selectCatName"),
"", "",
function(this: HTMLInputElement){ function(this: HTMLInputElement){
name = this.value; name = this.value;
@ -688,7 +691,7 @@ class Guild extends SnowFlake{
[ [
"button", "button",
"", "",
"submit", I18n.getTranslation("submit"),
function(this:Guild){ function(this:Guild){
console.log(name, category); console.log(name, category);
this.createChannel(name, category); this.createChannel(name, category);

View file

@ -1,3 +1,4 @@
import { I18n } from "./i18n.js";
import{ mobile }from"./login.js"; import{ mobile }from"./login.js";
console.log(mobile); console.log(mobile);
const serverbox = document.getElementById("instancebox") as HTMLDivElement; const serverbox = document.getElementById("instancebox") as HTMLDivElement;
@ -5,7 +6,7 @@ const serverbox = document.getElementById("instancebox") as HTMLDivElement;
fetch("/instances.json") fetch("/instances.json")
.then(_=>_.json()) .then(_=>_.json())
.then( .then(
( async (
json: { json: {
name: string; name: string;
description?: string; description?: string;
@ -24,6 +25,7 @@ fetch("/instances.json")
}; };
}[] }[]
)=>{ )=>{
await I18n.done;
console.warn(json); console.warn(json);
for(const instance of json){ for(const instance of json){
if(instance.display === false){ if(instance.display === false){
@ -66,11 +68,11 @@ fetch("/instances.json")
const stats = document.createElement("div"); const stats = document.createElement("div");
stats.classList.add("flexltr"); stats.classList.add("flexltr");
const span = document.createElement("span"); const span = document.createElement("span");
span.innerText = `Uptime: All time: ${Math.round( span.innerText = I18n.getTranslation("home.uptimeStats",Math.round(
instance.uptime.alltime * 100 instance.uptime.alltime * 100
)}% This week: ${Math.round( )+"",Math.round(
instance.uptime.weektime * 100 instance.uptime.weektime * 100
)}% Today: ${Math.round(instance.uptime.daytime * 100)}%`; )+"",Math.round(instance.uptime.daytime * 100)+"")
stats.append(span); stats.append(span);
statbox.append(stats); statbox.append(stats);
} }
@ -79,7 +81,7 @@ fetch("/instances.json")
if(instance.online){ if(instance.online){
window.location.href = "/register.html?instance=" + encodeURI(instance.name); window.location.href = "/register.html?instance=" + encodeURI(instance.name);
}else{ }else{
alert("Instance is offline, can't connect"); alert(I18n.getTranslation("home.warnOffiline"));
} }
}; };
serverbox.append(div); serverbox.append(div);

View file

@ -105,4 +105,5 @@ class I18n{
} }
} }
} }
I18n.create("/translations/en.json","en")
export{I18n}; export{I18n};

View file

@ -4,6 +4,7 @@ import{ mobile, getBulkUsers, setTheme, Specialuser }from"./login.js";
import{ MarkDown }from"./markdown.js"; import{ MarkDown }from"./markdown.js";
import{ Message }from"./message.js"; import{ Message }from"./message.js";
import{ File }from"./file.js"; import{ File }from"./file.js";
import { I18n } from "./i18n.js";
(async ()=>{ (async ()=>{
async function waitForLoad(): Promise<void>{ async function waitForLoad(): Promise<void>{
@ -13,7 +14,7 @@ import{ File }from"./file.js";
} }
await waitForLoad(); await waitForLoad();
await I18n.done
const users = getBulkUsers(); const users = getBulkUsers();
if(!users.currentuser){ if(!users.currentuser){
window.location.href = "/login.html"; window.location.href = "/login.html";
@ -74,7 +75,7 @@ import{ File }from"./file.js";
const switchAccountDiv = document.createElement("div"); const switchAccountDiv = document.createElement("div");
switchAccountDiv.classList.add("switchtable"); switchAccountDiv.classList.add("switchtable");
switchAccountDiv.textContent = "Switch accounts ⇌"; switchAccountDiv.textContent = I18n.getTranslation("switchAccounts");
switchAccountDiv.addEventListener("click", ()=>{ switchAccountDiv.addEventListener("click", ()=>{
window.location.href = "/login.html"; window.location.href = "/login.html";
}); });
@ -93,9 +94,7 @@ import{ File }from"./file.js";
showAccountSwitcher(); showAccountSwitcher();
}); });
const switchAccountsElement = document.getElementById( const switchAccountsElement = document.getElementById("switchaccounts") as HTMLDivElement;
"switchaccounts"
) as HTMLDivElement;
switchAccountsElement.addEventListener("click", event=>{ switchAccountsElement.addEventListener("click", event=>{
event.stopImmediatePropagation(); event.stopImmediatePropagation();
showAccountSwitcher(); showAccountSwitcher();
@ -115,14 +114,13 @@ import{ File }from"./file.js";
}); });
}catch(e){ }catch(e){
console.error(e); console.error(e);
(document.getElementById("load-desc") as HTMLSpanElement).textContent = (document.getElementById("load-desc") as HTMLSpanElement).textContent = I18n.getTranslation("accountNotStart");
"Account unable to start";
thisUser = new Localuser(-1); thisUser = new Localuser(-1);
} }
const menu = new Contextmenu("create rightclick"); const menu = new Contextmenu<void,void>("create rightclick");
menu.addbutton( menu.addbutton(
"Create channel", I18n.getTranslation("channel.createChannel"),
()=>{ ()=>{
if(thisUser.lookingguild){ if(thisUser.lookingguild){
thisUser.lookingguild.createchannels(); thisUser.lookingguild.createchannels();
@ -133,7 +131,7 @@ import{ File }from"./file.js";
); );
menu.addbutton( menu.addbutton(
"Create category", I18n.getTranslation("channel.createCatagory"),
()=>{ ()=>{
if(thisUser.lookingguild){ if(thisUser.lookingguild){
thisUser.lookingguild.createcategory(); thisUser.lookingguild.createcategory();
@ -143,15 +141,9 @@ import{ File }from"./file.js";
()=>thisUser.isAdmin() ()=>thisUser.isAdmin()
); );
menu.bindContextmenu( menu.bindContextmenu(document.getElementById("channels") as HTMLDivElement);
document.getElementById("channels") as HTMLDivElement,
0,
0
);
const pasteImageElement = document.getElementById( const pasteImageElement = document.getElementById("pasteimage") as HTMLDivElement;
"pasteimage"
) as HTMLDivElement;
let replyingTo: Message | null = null; let replyingTo: Message | null = null;
async function handleEnter(event: KeyboardEvent): Promise<void>{ async function handleEnter(event: KeyboardEvent): Promise<void>{

View file

@ -65,7 +65,6 @@ class Localuser{
"Content-type": "application/json; charset=UTF-8", "Content-type": "application/json; charset=UTF-8",
Authorization: this.userinfo.token, Authorization: this.userinfo.token,
}; };
I18n.create("/translations/en.json","en")
} }
async gottenReady(ready: readyjson): Promise<void>{ async gottenReady(ready: readyjson): Promise<void>{
await I18n.done; await I18n.done;

View file

@ -56,13 +56,13 @@ class Message extends SnowFlake{
Message.setupcmenu(); Message.setupcmenu();
} }
static setupcmenu(){ static setupcmenu(){
Message.contextmenu.addbutton(I18n.getTranslation.bind(I18n,"copyrawtext"), function(this: Message){ Message.contextmenu.addbutton(()=>I18n.getTranslation("copyrawtext"), function(this: Message){
navigator.clipboard.writeText(this.content.rawString); navigator.clipboard.writeText(this.content.rawString);
}); });
Message.contextmenu.addbutton(I18n.getTranslation.bind(I18n,"reply"), function(this: Message){ Message.contextmenu.addbutton(()=>I18n.getTranslation("reply"), function(this: Message){
this.channel.setReplying(this); this.channel.setReplying(this);
}); });
Message.contextmenu.addbutton(I18n.getTranslation.bind(I18n,"copymessageid"), function(this: Message){ Message.contextmenu.addbutton(()=>I18n.getTranslation("copymessageid"), function(this: Message){
navigator.clipboard.writeText(this.id); navigator.clipboard.writeText(this.id);
}); });
Message.contextmenu.addsubmenu( Message.contextmenu.addsubmenu(

View file

@ -938,15 +938,18 @@ class Form implements OptionsElement<object>{
(this.button.deref() as HTMLElement).hidden=false; (this.button.deref() as HTMLElement).hidden=false;
} }
} }
selectMap=new WeakMap<SelectInput,string[]>();
addSelect( addSelect(
label: string, label: string,
formName: string, formName: string,
selections: string[], selections: string[],
{ defaultIndex = 0, required = false } = {} { defaultIndex = 0, required = false}={},
correct:string[]=selections
){ ){
const select = this.options.addSelect(label, _=>{}, selections, { const select = this.options.addSelect(label, _=>{}, selections, {
defaultIndex, defaultIndex,
}); });
this.selectMap.set(select,correct);
this.names.set(formName, select); this.names.set(formName, select);
if(required){ if(required){
this.required.add(select); this.required.add(select);
@ -1110,7 +1113,7 @@ class Form implements OptionsElement<object>{
if(thing === "")continue; if(thing === "")continue;
const input = this.names.get(thing) as OptionsElement<any>; const input = this.names.get(thing) as OptionsElement<any>;
if(input instanceof SelectInput){ if(input instanceof SelectInput){
(build as any)[thing] = input.options[input.value]; (build as any)[thing] = (this.selectMap.get(input) as string[])[input.value];
continue; continue;
}else if(input instanceof FileInput){ }else if(input instanceof FileInput){
const options = this.fileOptions.get(input); const options = this.fileOptions.get(input);

View file

@ -122,7 +122,101 @@
"no":"No", "no":"No",
"todayAt":"Today at $1", "todayAt":"Today at $1",
"yesterdayAt":"Yesterday at $1", "yesterdayAt":"Yesterday at $1",
"otherAt":"$1 at $2" "otherAt":"$1 at $2",
"botSettings":"Bot Settings",
"uploadPfp":"Upload pfp:",
"uploadBanner":"Upload banner:",
"pronouns":"Pronouns:",
"bio":"Bio:",
"profileColor":"Profile color",
"botGuilds":"Guilds bot is in:",
"leaveGuild":"Leave Guild",
"confirmGuildLeave":"Are you sure you want to leave $1",
"UrlGen":"URL generator",
"typing":"$2 {{PLURAL:$1|are|is}} typing",
"noMessages":"No messages appear to be here, be the first to say something!",
"blankMessage":"Blank Message",
"channel":{
"copyId":"Copy channel id",
"markRead":"Mark as read",
"settings":"Settings",
"delete":"Delete channel",
"makeInvite":"Make invite",
"settingsFor":"Settings for $1",
"voice":"Voice",
"text":"Text",
"announcement":"Announcements",
"name:":"Name:",
"topic:":"Topic:",
"nsfw:":"NSFW:",
"selectType":"Select channel type",
"selectName":"Name of channel",
"selectCatName":"Name of channel",
"createChannel":"Create channel",
"createCatagory":"Create category"
},
"switchAccounts":"Switch accounts ⇌",
"accountNotStart":"Account unable to start",
"home":{
"uptimeStats":"Uptime: \n All time: $1\nThis week: $2\nToday: $3",
"warnOffiline":"Instance is offline, can't connect"
},
"submit":"submit",
"guild":{
"copyId":"Copy guild id",
"markRead":"Mark as read",
"notifications":"Notifications",
"leave":"Leave guild",
"settings":"Settings",
"delete":"Delete guild",
"makeInvite":"Make invite",
"settingsFor":"Settings for $1",
"name:":"Name:",
"topic:":"Topic:",
"icon:":"Icon:",
"overview":"Overview",
"banner:":"Banner:",
"region:":"Region:",
"roles":"Roles",
"selectnoti":"Select notifications type",
"all":"all",
"onlyMentions":"only mentions",
"none":"node",
"confirmLeave":"Are you sure you want to leave?",
"yesLeave":"Yes, I'm sure",
"noLeave":"Nevermind",
"confirmDelete":"Are you sure you want to delete $1?",
"serverName":"Name of server:",
"yesDelete":"Yes, I'm sure",
"noDelete":"Nevermind"
},
"inviteOptions":{
"title":"Invite People",
"30m":"30 Minutes",
"1h":"1 Hour",
"6h":"6 Hours",
"12h":"12 Hours",
"1d":"1 Day",
"7d":"7 Days",
"30d":"30 Days",
"never":"Never",
"limit":"$1 {{PLURAL:$1|use|uses}}",
"noLimit":"No limit"
},
"invite":{
"invitedBy":"You've been invited by $1",
"alreadyJoined":"Already joined",
"accept":"Accept"
},
"replyingTo":"Replying to $1",
"DMs":{
"copyId":"Copy DM id",
"markRead":"Mark as read",
"close":"Close DM"
},
"user":{
"copyId":"Copy user ID"
}
}, },
"ru": "./ru.json" "ru": "./ru.json"
} }