further translation support

A few more things need to be done, but it's getting a lot closer
This commit is contained in:
MathMan05 2024-11-01 12:16:47 -05:00
parent 4e7d181a14
commit 00c105db28
10 changed files with 194 additions and 106 deletions

View file

@ -36,7 +36,7 @@ class I18n{
for(const json of this.translations){
let jsont:string|translation=json;
for(const thing of path){
if(typeof jsont !== "string" ){
if(typeof jsont !== "string" && jsont!==undefined){
jsont=jsont[thing];
}else{

View file

@ -953,7 +953,7 @@ class Localuser{
content.innerHTML = "";
const title = document.createElement("h2");
title.textContent = I18n.getTranslation("guild.disoveryTitle");
title.textContent = I18n.getTranslation("guild.disoveryTitle",json.guilds.length+"");
content.appendChild(title);
const guilds = document.createElement("div");
@ -1365,7 +1365,7 @@ class Localuser{
);
form.addTextInput(I18n.getTranslation("lcoaluser.newDiscriminator"), "discriminator");
});
security.addButtonInput("", I18n.getTranslation("lcoaluser.changeEmail"), ()=>{
security.addButtonInput("", I18n.getTranslation("localuser.changeEmail"), ()=>{
const form = security.addSubForm(
I18n.getTranslation("lcoaluser.changeEmail"),
_=>{
@ -1401,9 +1401,9 @@ class Localuser{
}
form.addTextInput(I18n.getTranslation("localuser.newUsername"), "username");
});
security.addButtonInput("", "Change password", ()=>{
security.addButtonInput("", I18n.getTranslation("localuser.changePassword"), ()=>{
const form = security.addSubForm(
"Change Password",
I18n.getTranslation("localuser.changePassword"),
_=>{
security.returnFromSub();
},
@ -1413,13 +1413,13 @@ class Localuser{
method: "PATCH",
}
);
form.addTextInput("Old password:", "password", { password: true });
form.addTextInput(I18n.getTranslation("localuser.oldPassword:"), "password", { password: true });
if(this.mfa_enabled){
form.addTextInput("Code:", "code");
form.addTextInput(I18n.getTranslation("localuser.2faCode"), "code");
}
let in1 = "";
let in2 = "";
form.addTextInput("New password:", "").watchForChange(text=>{
form.addTextInput(I18n.getTranslation("localuser.newPassword:"), "").watchForChange(text=>{
in1 = text;
});
const copy = form.addTextInput("New password again:", "");
@ -1430,7 +1430,7 @@ class Localuser{
if(in1 === in2){
return in1;
}else{
throw new FormError(copy, "Passwords don't match");
throw new FormError(copy, I18n.getTranslation("localuser.PasswordsNoMatch"));
}
});
});
@ -1473,8 +1473,7 @@ class Localuser{
});
}else{
container.classList.add("disabled");
container.title =
"This connection has been disabled server-side.";
container.title = I18n.getTranslation("localuser.PasswordsNoMatch");
}
connectionContainer.appendChild(container);
@ -1483,16 +1482,16 @@ class Localuser{
connections.addHTMLArea(connectionContainer);
}
{
const devPortal = settings.addButton("Developer Portal");
const devPortal = settings.addButton(I18n.getTranslation("localuser.devPortal"));
fetch(this.info.api + "/teams", {
headers: this.headers,
}).then(async (teamsRes)=>{
const teams = await teamsRes.json();
devPortal.addButtonInput("", "Create application", ()=>{
devPortal.addButtonInput("", I18n.getTranslation("localuser.createApp"), ()=>{
const form = devPortal.addSubForm(
"Create application",
I18n.getTranslation("localuser.createApp"),
(json: any)=>{
if(json.message) form.error("name", json.message);
else{
@ -1509,7 +1508,7 @@ class Localuser{
form.addTextInput("Name:", "name", { required: true });
form.addSelect(
"Team:",
I18n.getTranslation("localuser.team:"),
"team_id",
["Personal", ...teams.map((team: { name: string })=>team.name)],
{
@ -1583,16 +1582,16 @@ class Localuser{
headers:this.headers,
traditionalSubmit:true
});
form.addTextInput("Application name:","name",{initText:json.name});
form.addMDInput("Description:","description",{initText:json.description});
form.addTextInput(I18n.getTranslation("localuser.appName"),"name",{initText:json.name});
form.addMDInput(I18n.getTranslation("localuser.description"),"description",{initText:json.description});
form.addFileInput("Icon:","icon");
form.addTextInput("Privacy policy URL:","privacy_policy_url",{initText:json.privacy_policy_url});
form.addTextInput("Terms of Service URL:","terms_of_service_url",{initText:json.terms_of_service_url});
form.addCheckboxInput("Make bot publicly inviteable?","bot_public",{initState:json.bot_public});
form.addCheckboxInput("Require code grant to invite the bot?","bot_require_code_grant",{initState:json.bot_require_code_grant});
form.addButtonInput("",(json.bot ? "Manage" : "Add")+" bot",async ()=>{
form.addTextInput(I18n.getTranslation("localuser.privacyPolcyURL"),"privacy_policy_url",{initText:json.privacy_policy_url});
form.addTextInput(I18n.getTranslation("localuser.TOSURL"),"terms_of_service_url",{initText:json.terms_of_service_url});
form.addCheckboxInput(I18n.getTranslation("localuser.publicAvaliable"),"bot_public",{initState:json.bot_public});
form.addCheckboxInput(I18n.getTranslation("localuser.requireCode"),"bot_require_code_grant",{initState:json.bot_require_code_grant});
form.addButtonInput("",I18n.getTranslation("localuser."+(json.bot?"manageBot":"addBot")),async ()=>{
if(!json.bot){
if(!confirm("Are you sure you want to add a bot to this application? There's no going back.")){
if(!confirm(I18n.getTranslation("localuser.confirmAddBot"))){
return;
}
const updateRes = await fetch(
@ -1614,19 +1613,19 @@ class Localuser{
});
const json = await res.json();
if(!json.bot){
return alert("For some reason, this application doesn't have a bot (yet).");
return alert(I18n.getTranslation("localuser.confuseNoBot"));
}
const bot:mainuserjson=json.bot;
const form=container.addSubForm("Editing bot "+bot.username,out=>{console.log(out)},{
const form=container.addSubForm(I18n.getTranslation("localuser.editingBot",bot.username),out=>{console.log(out)},{
method:"PATCH",
fetchURL:this.info.api + "/applications/" + appId + "/bot",
headers:this.headers,
traditionalSubmit:true
});
form.addTextInput("Bot username:","username",{initText:bot.username});
form.addFileInput("Bot avatar:","avatar");
form.addButtonInput("","Reset Token",async ()=>{
if(!confirm("Are you sure you want to reset the bot token? Your bot will stop working until you update it.")){
form.addTextInput(I18n.getTranslation("localuser.botUsername"),"username",{initText:bot.username});
form.addFileInput(I18n.getTranslation("localuser.botAvatar"),"avatar");
form.addButtonInput("",I18n.getTranslation("localuser.resetToken"),async ()=>{
if(!confirm(I18n.getTranslation("localuser.confirmReset"))){
return;
}
const updateRes = await fetch(
@ -1637,27 +1636,27 @@ class Localuser{
}
);
const updateJSON = await updateRes.json();
text.setText("Token: "+updateJSON.token);
text.setText(I18n.getTranslation("localuser.tokenDisplay",updateJSON.token));
this.botTokens.set(appId,updateJSON.token);
if(this.perminfo.applications[appId]){
this.perminfo.applications[appId]=updateJSON.token;
this.userinfo.updateLocal();
}
});
const text=form.addText(this.botTokens.has(appId)?"Token: "+this.botTokens.get(appId):"Token: *****************");
const text=form.addText(I18n.getTranslation("localuser.tokenDisplay",this.botTokens.has(appId)?this.botTokens.get(appId) as string:"*****************") );
const check=form.addOptions("",{noSubmit:true});
if(!this.perminfo.applications){
this.perminfo.applications={};
this.userinfo.updateLocal();
}
const checkbox=check.addCheckboxInput("Save token to localStorage",()=>{},{initState:!!this.perminfo.applications[appId]});
const checkbox=check.addCheckboxInput(I18n.getTranslation("localuser.saveToken"),()=>{},{initState:!!this.perminfo.applications[appId]});
checkbox.watchForChange(_=>{
if(_){
if(this.botTokens.has(appId)){
this.perminfo.applications[appId]=this.botTokens.get(appId);
this.userinfo.updateLocal();
}else{
alert("Don't know token so can't save it to localStorage, sorry");
alert(I18n.getTranslation("localuser.noToken"));
checkbox.setState(false);
}
}else{
@ -1665,14 +1664,14 @@ class Localuser{
this.userinfo.updateLocal();
}
});
form.addButtonInput("","Advanced Bot Settings",()=>{
form.addButtonInput("",I18n.getTranslation("localuser.advancedBot"),()=>{
const token=this.botTokens.get(appId);
if(token){
const botc=new Bot(bot,token,this);
botc.settings();
}
});
form.addButtonInput("","Bot Invite Creator",()=>{
form.addButtonInput("",I18n.getTranslation("localuser.botInviteCreate"),()=>{
Bot.InviteMaker(appId,form,this.info);
})
}
@ -1849,11 +1848,11 @@ class Localuser{
const dialog = new Dialog([
"vdiv",
["title", "Instance stats: " + this.instancePing.name],
["text", "Registered users: " + json.counts.user],
["text", "Servers: " + json.counts.guild],
["text", "Messages: " + json.counts.message],
["text", "Members: " + json.counts.members],
["title", I18n.getTranslation("instanceStats.name",this.instancePing.name) ],
["text", I18n.getTranslation("instanceStats.users",json.counts.user)],
["text", I18n.getTranslation("instanceStats.servers",json.counts.guild)],
["text", I18n.getTranslation("instanceStats.messages",json.counts.message)],
["text", I18n.getTranslation("instanceStats.members",json.counts.members)],
]);
dialog.show();
}

View file

@ -1,4 +1,5 @@
import{ Dialog }from"./dialog.js";
import { I18n } from "./i18n.js";
const mobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
const iOS = /iPhone|iPad|iPod/i.test(navigator.userAgent);
@ -338,7 +339,7 @@ async function getapiurls(str: string): Promise<
async function checkInstance(instance?: string){
const verify = document.getElementById("verify");
try{
verify!.textContent = "Checking Instance";
verify!.textContent = I18n.getTranslation("login.checking");
const instanceValue = instance || (instancein as HTMLInputElement).value;
const instanceinfo = (await getapiurls(instanceValue)) as {
wellknown: string;
@ -351,7 +352,7 @@ async function checkInstance(instance?: string){
if(instanceinfo){
instanceinfo.value = instanceValue;
localStorage.setItem("instanceinfo", JSON.stringify(instanceinfo));
verify!.textContent = "Instance is all good";
verify!.textContent = I18n.getTranslation("login.allGood");
// @ts-ignore
if(checkInstance.alt){
// @ts-ignore
@ -362,11 +363,11 @@ async function checkInstance(instance?: string){
verify!.textContent = "";
}, 3000);
}else{
verify!.textContent = "Invalid Instance, try again";
verify!.textContent = I18n.getTranslation("login.invalid");
}
}catch{
console.log("catch");
verify!.textContent = "Invalid Instance, try again";
verify!.textContent = I18n.getTranslation("login.invalid");
}
}
@ -374,7 +375,7 @@ if(instancein){
console.log(instancein);
instancein.addEventListener("keydown", ()=>{
const verify = document.getElementById("verify");
verify!.textContent = "Waiting to check Instance";
verify!.textContent = I18n.getTranslation("login.waiting");
if(timeout !== null && typeof timeout !== "string"){
clearTimeout(timeout);
}
@ -442,7 +443,7 @@ async function login(username: string, password: string, captcha: string){
let onetimecode = "";
new Dialog([
"vdiv",
["title", "2FA code:"],
["title", I18n.getTranslation("2faCode")],
[
"textbox",
"",
@ -455,7 +456,7 @@ async function login(username: string, password: string, captcha: string){
[
"button",
"",
"Submit",
I18n.getTranslation("submit"),
function(){
fetch(api + "/auth/mfa/totp", {
method: "POST",

View file

@ -2,6 +2,7 @@ import{ Channel }from"./channel.js";
import{ Dialog }from"./dialog.js";
import{ Emoji }from"./emoji.js";
import{ Guild }from"./guild.js";
import { I18n } from "./i18n.js";
import{ Localuser }from"./localuser.js";
import{ Member }from"./member.js";
@ -9,8 +10,8 @@ class MarkDown{
txt: string[];
keep: boolean;
stdsize: boolean;
owner: Localuser | Channel;
info: Localuser["info"];
owner: Localuser | Channel|void;
info: Localuser["info"]|void=undefined;
constructor(
text: string | string[],
owner: MarkDown["owner"],
@ -24,7 +25,9 @@ class MarkDown{
if(this.txt === undefined){
this.txt = [];
}
if(owner){
this.info = owner.info;
}
this.keep = keep;
this.owner = owner;
this.stdsize = stdsize;
@ -32,9 +35,10 @@ class MarkDown{
get localuser(){
if(this.owner instanceof Localuser){
return this.owner;
}else{
}else if(this.owner){
return this.owner.localuser;
}
return null;
}
get rawString(){
return this.txt.join("");
@ -437,7 +441,7 @@ txt[j + 1] === undefined)
continue;
}
}
if(txt[i] === "<" && (txt[i + 1] === "@" || txt[i + 1] === "#")){
if((txt[i] === "<" && (txt[i + 1] === "@" || txt[i + 1] === "#"))&&this.localuser){
let id = "";
let j = i + 2;
const numbers = new Set(["0","1","2","3","4","5","6","7","8","9",]);
@ -485,6 +489,7 @@ txt[j + 1] === undefined)
mention.textContent = `#${channel.name}`;
if(!keep){
mention.onclick = _=>{
if(!this.localuser) return;
this.localuser.goToChannel(id);
};
}
@ -586,7 +591,7 @@ txt[j + 1] === undefined)
if(
txt[i] === "<" &&
(txt[i + 1] === ":" || (txt[i + 1] === "a" && txt[i + 2] === ":"))
(txt[i + 1] === ":" || (txt[i + 1] === "a" && txt[i + 2] === ":")&&this.owner)
){
let found = false;
const build = txt[i + 1] === "a" ? ["<", "a", ":"] : ["<", ":"];
@ -609,6 +614,7 @@ txt[j + 1] === undefined)
const isEmojiOnly = txt.join("").trim() === buildjoin.trim();
const owner =
this.owner instanceof Channel ? this.owner.guild : this.owner;
if(!owner) continue;
const emoji = new Emoji(
{ name: buildjoin, id: parts[2], animated: Boolean(parts[1]) },
owner
@ -765,20 +771,18 @@ txt[j + 1] === undefined)
}else{
const full: Dialog = new Dialog([
"vdiv",
["title", "You're leaving Spacebar"],
["title", I18n.getTranslation("leaving")],
[
"text",
"You're going to " +
Url.host +
". Are you sure you want to go there?",
I18n.getTranslation("goingToURL",Url.host)
],
[
"hdiv",
["button", "", "Nevermind", (_: any)=>full.hide()],
["button", "", I18n.getTranslation("nevermind"), (_: any)=>full.hide()],
[
"button",
"",
"Go there",
I18n.getTranslation("goThere"),
(_: any)=>{
open();
full.hide();
@ -787,7 +791,7 @@ txt[j + 1] === undefined)
[
"button",
"",
"Go there and trust in the future",
I18n.getTranslation("goThereTrust"),
(_: any)=>{
open();
full.hide();

View file

@ -4,6 +4,7 @@ import{ Guild }from"./guild.js";
import{ SnowFlake }from"./snowflake.js";
import{ memberjson, presencejson }from"./jsontypes.js";
import{ Dialog }from"./dialog.js";
import { I18n } from "./i18n.js";
class Member extends SnowFlake{
static already = {};
@ -213,10 +214,10 @@ class Member extends SnowFlake{
let reason = "";
const menu = new Dialog([
"vdiv",
["title", "Kick " + this.name + " from " + this.guild.properties.name],
["title", I18n.getTranslation("member.kick",this.name,this.guild.properties.name)],
[
"textbox",
"Reason:",
I18n.getTranslation("member.reason:"),
"",
function(e: Event){
reason = (e.target as HTMLInputElement).value;
@ -225,7 +226,7 @@ class Member extends SnowFlake{
[
"button",
"",
"submit",
I18n.getTranslation("submit"),
()=>{
this.kickAPI(reason);
menu.hide();
@ -246,10 +247,10 @@ class Member extends SnowFlake{
let reason = "";
const menu = new Dialog([
"vdiv",
["title", "Ban " + this.name + " from " + this.guild.properties.name],
["title", I18n.getTranslation("member.ban",this.name,this.guild.properties.name)],
[
"textbox",
"Reason:",
I18n.getTranslation("member.reason",this.name,this.guild.properties.name),
"",
function(e: Event){
reason = (e.target as HTMLInputElement).value;
@ -258,7 +259,7 @@ class Member extends SnowFlake{
[
"button",
"",
"submit",
I18n.getTranslation("submit",this.name,this.guild.properties.name),
()=>{
this.banAPI(reason);
menu.hide();

View file

@ -1,4 +1,6 @@
import { I18n } from "./i18n.js";
import{ checkInstance, adduser }from"./login.js";
import { MarkDown } from "./markdown.js";
const registerElement = document.getElementById("register");
if(registerElement){
@ -18,8 +20,7 @@ async function registertry(e: Event){
const captchaKey = (elements[7] as HTMLInputElement)?.value;
if(password !== confirmPassword){
(document.getElementById("wrong") as HTMLElement).textContent =
"Passwords don't match";
(document.getElementById("wrong") as HTMLElement).textContent = I18n.getTranslation("localuser.PasswordsNoMatch");
return;
}
@ -83,22 +84,22 @@ function handleErrors(errors: any, elements: HTMLFormControlsCollection){
}else if(errors.password){
showError(
elements[3] as HTMLElement,
"Password: " + errors.password._errors[0].message
I18n.getTranslation("register.passwordError",errors.password._errors[0].message)
);
}else if(errors.username){
showError(
elements[2] as HTMLElement,
"Username: " + errors.username._errors[0].message
I18n.getTranslation("register.usernameError",errors.username._errors[0].message)
);
}else if(errors.email){
showError(
elements[1] as HTMLElement,
"Email: " + errors.email._errors[0].message
I18n.getTranslation("register.emailError",errors.email._errors[0].message)
);
}else if(errors.date_of_birth){
showError(
elements[5] as HTMLElement,
"Date of Birth: " + errors.date_of_birth._errors[0].message
I18n.getTranslation("register.DOBError",errors.date_of_birth._errors[0].message)
);
}else{
(document.getElementById("wrong") as HTMLElement).textContent =
@ -125,8 +126,6 @@ function showError(element: HTMLElement, message: string){
errorElement.textContent = message;
}
let TOSa = document.getElementById("TOSa") as HTMLAnchorElement | null;
async function tosLogic(){
const instanceInfo = JSON.parse(localStorage.getItem("instanceinfo") ?? "{}");
const apiurl = new URL(instanceInfo.api);
@ -136,14 +135,12 @@ async function tosLogic(){
const tosPage = data.instance.tosPage;
if(tosPage){
document.getElementById("TOSbox")!.innerHTML =
"I agree to the <a href=\"\" id=\"TOSa\">Terms of Service</a>:";
TOSa = document.getElementById("TOSa") as HTMLAnchorElement;
TOSa.href = tosPage;
const box=document.getElementById("TOSbox");
if(!box) return;
box.innerHTML ="";
box.append(new MarkDown(I18n.getTranslation("register.agreeTOS",tosPage)).makeHTML());
}else{
document.getElementById("TOSbox")!.textContent =
"This instance has no Terms of Service, accept ToS anyways:";
TOSa = null;
document.getElementById("TOSbox")!.textContent =I18n.getTranslation("register.noTOS");
}
console.log(tosPage);
}

View file

@ -142,6 +142,7 @@ class PermissionToggle implements OptionsElement<number>{
import{ OptionsElement, Buttons }from"./settings.js";
import { Contextmenu } from "./contextmenu.js";
import { Channel } from "./channel.js";
import { I18n } from "./i18n.js";
class RoleList extends Buttons{
permissions: [Role, Permissions][];
permission: Permissions;
@ -204,26 +205,26 @@ class RoleList extends Buttons{
this.redoButtons();
}
makeguildmenus(option:Options){
option.addButtonInput("","Display settings",()=>{
option.addButtonInput("",I18n.getTranslation("role.displaySettings"),()=>{
const role=this.guild.roleids.get(this.curid as string);
if(!role) return;
const form=option.addSubForm("Display settings",()=>{},{
const form=option.addSubForm(I18n.getTranslation("role.displaySettings"),()=>{},{
fetchURL:this.info.api+"/guilds/"+this.guild.id+"/roles/"+this.curid,
method:"PATCH",
headers:this.headers,
traditionalSubmit:true
});
form.addTextInput("Role Name:","name",{
form.addTextInput(I18n.getTranslation("role.name"),"name",{
initText:role.name
});
form.addCheckboxInput("Hoisted:","hoist",{
form.addCheckboxInput(I18n.getTranslation("role.hoisted"),"hoist",{
initState:role.hoist
});
form.addCheckboxInput("Allow anyone to ping this role:","mentionable",{
form.addCheckboxInput(I18n.getTranslation("role.mentionable"),"mentionable",{
initState:role.mentionable
});
const color="#"+role.color.toString(16).padStart(6,"0");
form.addColorInput("Color","color",{
form.addColorInput(I18n.getTranslation("role.color"),"color",{
initColor:color
});
form.addPreprocessor((obj:any)=>{
@ -236,7 +237,7 @@ class RoleList extends Buttons{
static guildrolemenu=this.GuildRoleMenu();
private static ChannelRoleMenu(){
const menu=new Contextmenu<RoleList,Role>("role settings");
menu.addbutton("Remove role",function(role){
menu.addbutton(()=>I18n.getTranslation("role.remove"),function(role){
if(!this.channel) return;
console.log(role);
fetch(this.info.api+"/channels/"+this.channel.id+"/permissions/"+role.id,{
@ -248,8 +249,8 @@ class RoleList extends Buttons{
}
private static GuildRoleMenu(){
const menu=new Contextmenu<RoleList,Role>("role settings");
menu.addbutton("Delete Role",function(role){
if(!confirm("Are you sure you want to delete "+role.name+"?")) return;
menu.addbutton(()=>I18n.getTranslation("role.delete"),function(role){
if(!confirm(I18n.getTranslation("role.confirmDelete"))) return;
console.log(role);
fetch(this.info.api+"/guilds/"+this.guild.id+"/roles/"+role.id,{
method:"DELETE",

View file

@ -1,3 +1,5 @@
import { I18n } from "./i18n.js";
interface OptionsElement<x> {
//
generateHTML(): HTMLElement;
@ -828,9 +830,9 @@ class Options implements OptionsElement<void>{
div.classList.add("flexltr", "savediv");
const span = document.createElement("span");
div.append(span);
span.textContent = "Careful, you have unsaved changes";
span.textContent = I18n.getTranslation("settings.unsaved");
const button = document.createElement("button");
button.textContent = "Save changes";
button.textContent = I18n.getTranslation("settings.save");
div.append(button);
this.haschanged = true;
this.owner.changed(div);
@ -886,7 +888,7 @@ class Form implements OptionsElement<object>{
onSubmit: (arg1: object) => void,
{
ltr = false,
submitText = "Submit",
submitText = I18n.getTranslation("submit"),
fetchURL = "",
headers = {},
method = "POST",
@ -919,7 +921,7 @@ class Form implements OptionsElement<object>{
onSubmit: (arg1: object) => void,
{
ltr = false,
submitText = "Submit",
submitText = I18n.getTranslation("submit"),
fetchURL = "",
headers = {},
method = "POST",

View file

@ -161,6 +161,19 @@
"uptimeStats":"Uptime: \n All time: $1\nThis week: $2\nToday: $3",
"warnOffiline":"Instance is offline, can't connect"
},
"register":{
"passwordError:":"Password: $1",
"usernameError":"Username: $1",
"emailError":"Email: $1",
"DOBError":"Date of Birth: $1",
"agreeTOS":"I agree to the [Terms of Service]($1):",
"noTOS":"This instance has no Terms of Service, accept ToS anyways:"
},
"leaving":"You're leaving Spacebar",
"goingToURL":"You're going to $1. Are you sure you want to go there?",
"goThere":"Go there",
"goThereTrust":"Go there and trust in the future",
"nevermind":"Nevermind",
"submit":"submit",
"guild":{
"copyId":"Copy guild id",
@ -191,7 +204,21 @@
"noDelete":"Nevermind",
"create":"Create guild",
"loadingDiscovery":"Loading...",
"disoveryTitle":"Guild discovery ($1) {{PLURAL:$1|entrie|entries}}"
"disoveryTitle":"Guild discovery ($1) {{PLURAL:$1|entry|entries}}"
},
"role":{
"displaySettings":"Display settings",
"name":"Role name:",
"hoisted":"Hoisted:",
"mentionable":"Allow anyone to ping this role:",
"color":"Color",
"remove":"Remove role",
"delete":"Delete Role",
"confirmDelete":"Are you sure you want to delete $1?"
},
"settings":{
"unsaved":"Careful, you have unsaved changes",
"save":"Save changes"
},
"localuser":{
"settings":"Settings",
@ -224,7 +251,42 @@
"password:":"Password",
"newEmail:":"New email",
"changeUsername":"Change username",
"newUsername":"New username:"
"newUsername":"New username:",
"changePassword":"Change password",
"oldPassword:":"Old password:",
"newPassword:":"New password:",
"PasswordsNoMatch":"Password don't match",
"disableConnection":"This connection has been disabled server-side",
"devPortal":"Developer Portal",
"createApp":"Create application",
"team:":"Team:",
"appName":"Application name:",
"description":"Description:",
"privacyPolcyURL":"Privacy policy URL:",
"TOSURL":"Terms of Service URL:",
"publicAvaliable":"Make bot publicly inviteable?",
"requireCode":"Require code grant to invite the bot?",
"manageBot":"Manage bot",
"addBot":"Add bot",
"confirmAddBot":"Are you sure you want to add a bot to this application? There's no going back.",
"confuseNoBot":"For some reason, this application doesn't have a bot (yet).",
"editingBot":"Editing bot $1",
"botUsername":"Bot username:",
"botAvatar":"Bot avatar:",
"resetToken":"Reset Token",
"confirmReset":"Are you sure you want to reset the bot token? Your bot will stop working until you update it.",
"tokenDisplay":"Token: $1",
"saveToken":"Save token to localStorage",
"noToken":"Don't know token so can't save it to localStorage, sorry",
"advancedBot":"Advanced Bot Settings",
"botInviteCreate":"Bot Invite Creator"
},
"instanceStats":{
"name":"Instance stats: $1",
"users":"Registered users: $1",
"servers":"Servers: $1",
"messages":"Messages: $1",
"members":"Members: $1"
},
"inviteOptions":{
"title":"Invite People",
@ -239,6 +301,7 @@
"limit":"$1 {{PLURAL:$1|use|uses}}",
"noLimit":"No limit"
},
"2faCode":"2FA code:",
"invite":{
"invitedBy":"You've been invited by $1",
"alreadyJoined":"Already joined",
@ -258,7 +321,26 @@
"user":{
"copyId":"Copy user ID",
"online":"Online",
"offline":"Offline"
"offline":"Offline",
"message":"Message user",
"block":"Block user",
"unblock":"unblock user",
"friendReq":"Friend request",
"kick":"Kick member",
"ban":"Ban member",
"addRole":"Add roles",
"removeRole":"Remove roles"
},
"login":{
"checking":"Checking Instance",
"allGood":"All good",
"invalid":"Invalid Instance, try again",
"waiting":"Waiting to check Instance"
},
"member":{
"kick":"Kick $1 from $2",
"reason:":"Reason:",
"ban":"Ban $1 from $2"
},
"errorReconnect":"Unable to connect to the server, retrying in <b>$1</b> seconds...",
"retrying":"Retrying...",

View file

@ -7,6 +7,7 @@ import{ SnowFlake }from"./snowflake.js";
import{ presencejson, userjson }from"./jsontypes.js";
import { Role } from "./role.js";
import { Search } from "./search.js";
import { I18n } from "./i18n.js";
class User extends SnowFlake{
owner: Localuser;
@ -96,10 +97,10 @@ class User extends SnowFlake{
static contextmenu = new Contextmenu<User, Member | undefined>("User Menu");
static setUpContextMenu(): void{
this.contextmenu.addbutton("Copy user id", function(this: User){
this.contextmenu.addbutton(()=>I18n.getTranslation("user.copyId"), function(this: User){
navigator.clipboard.writeText(this.id);
});
this.contextmenu.addbutton("Message user", function(this: User){
this.contextmenu.addbutton(()=>I18n.getTranslation("user.message"), function(this: User){
fetch(this.info.api + "/users/@me/channels", {
method: "POST",
body: JSON.stringify({ recipients: [this.id] }),
@ -111,7 +112,7 @@ class User extends SnowFlake{
});
});
this.contextmenu.addbutton(
"Block user",
()=>I18n.getTranslation("user.block"),
function(this: User){
this.block();
},
@ -122,7 +123,7 @@ class User extends SnowFlake{
);
this.contextmenu.addbutton(
"Unblock user",
()=>I18n.getTranslation("user.unblock"),
function(this: User){
this.unblock();
},
@ -131,7 +132,7 @@ class User extends SnowFlake{
return this.relationshipType === 2;
}
);
this.contextmenu.addbutton("Friend request", function(this: User){
this.contextmenu.addbutton(()=>I18n.getTranslation("user.friendReq"), function(this: User){
fetch(`${this.info.api}/users/@me/relationships/${this.id}`, {
method: "PUT",
headers: this.owner.headers,
@ -141,7 +142,7 @@ class User extends SnowFlake{
});
});
this.contextmenu.addbutton(
"Kick member",
()=>I18n.getTranslation("user.kick"),
function(this: User, member: Member | undefined){
member?.kick();
},
@ -159,7 +160,7 @@ class User extends SnowFlake{
}
);
this.contextmenu.addbutton(
"Ban member",
()=>I18n.getTranslation("user.ban"),
function(this: User, member: Member | undefined){
member?.ban();
},
@ -177,7 +178,7 @@ class User extends SnowFlake{
}
);
this.contextmenu.addbutton(
"Add roles",
()=>I18n.getTranslation("user.addRole"),
async function(this: User, member: Member | undefined,e){
if(member){
e.stopPropagation();
@ -203,7 +204,7 @@ class User extends SnowFlake{
}
);
this.contextmenu.addbutton(
"Remove roles",
()=>I18n.getTranslation("user.removeRole"),
async function(this: User, member: Member | undefined,e){
if(member){
e.stopPropagation();