add rights and a few menus to go along with that
This commit is contained in:
parent
b944be96a4
commit
11d2a8cb32
7 changed files with 339 additions and 9 deletions
|
@ -29,6 +29,7 @@ import {Emoji} from "./emoji.js";
|
||||||
import {Play} from "./audio/play.js";
|
import {Play} from "./audio/play.js";
|
||||||
import {Message} from "./message.js";
|
import {Message} from "./message.js";
|
||||||
import {badgeArr} from "./Dbadges.js";
|
import {badgeArr} from "./Dbadges.js";
|
||||||
|
import {Rights} from "./rights.js";
|
||||||
|
|
||||||
const wsCodesRetry = new Set([4000, 4001, 4002, 4003, 4005, 4007, 4008, 4009]);
|
const wsCodesRetry = new Set([4000, 4001, 4002, 4003, 4005, 4007, 4008, 4009]);
|
||||||
|
|
||||||
|
@ -79,11 +80,13 @@ class Localuser {
|
||||||
this.play = _;
|
this.play = _;
|
||||||
});
|
});
|
||||||
if (userinfo === -1) {
|
if (userinfo === -1) {
|
||||||
|
this.rights = new Rights("");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.token = userinfo.token;
|
this.token = userinfo.token;
|
||||||
this.userinfo = userinfo;
|
this.userinfo = userinfo;
|
||||||
this.perminfo.guilds ??= {};
|
this.perminfo.guilds ??= {};
|
||||||
|
this.perminfo.user ??= {};
|
||||||
this.serverurls = this.userinfo.serverurls;
|
this.serverurls = this.userinfo.serverurls;
|
||||||
this.initialized = false;
|
this.initialized = false;
|
||||||
this.info = this.serverurls;
|
this.info = this.serverurls;
|
||||||
|
@ -91,6 +94,8 @@ class Localuser {
|
||||||
"Content-type": "application/json; charset=UTF-8",
|
"Content-type": "application/json; charset=UTF-8",
|
||||||
Authorization: this.userinfo.token,
|
Authorization: this.userinfo.token,
|
||||||
};
|
};
|
||||||
|
const rights = this.perminfo.user.rights || "875069521787904";
|
||||||
|
this.rights = new Rights(rights);
|
||||||
}
|
}
|
||||||
async gottenReady(ready: readyjson): Promise<void> {
|
async gottenReady(ready: readyjson): Promise<void> {
|
||||||
await I18n.done;
|
await I18n.done;
|
||||||
|
@ -406,6 +411,11 @@ class Localuser {
|
||||||
await promise;
|
await promise;
|
||||||
}
|
}
|
||||||
relationshipsUpdate = () => {};
|
relationshipsUpdate = () => {};
|
||||||
|
rights: Rights;
|
||||||
|
updateRights(rights: string | number) {
|
||||||
|
this.rights.update(rights);
|
||||||
|
this.perminfo.user.rights = rights;
|
||||||
|
}
|
||||||
async handleEvent(temp: wsjson) {
|
async handleEvent(temp: wsjson) {
|
||||||
console.debug(temp);
|
console.debug(temp);
|
||||||
if (temp.s) this.lastSequence = temp.s;
|
if (temp.s) this.lastSequence = temp.s;
|
||||||
|
@ -1776,6 +1786,135 @@ class Localuser {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
this.rights.hasPermission("OPERATOR") ||
|
||||||
|
this.rights.hasPermission("CREATE_REGISTRATION_TOKENS")
|
||||||
|
) {
|
||||||
|
const manageInstance = settings.addButton(I18n.localuser.manageInstance());
|
||||||
|
if (this.rights.hasPermission("OPERATOR")) {
|
||||||
|
manageInstance.addButtonInput("", I18n.manageInstance.stop(), () => {
|
||||||
|
const menu = new Dialog("");
|
||||||
|
const options = menu.float.options;
|
||||||
|
options.addTitle(I18n.manageInstance.AreYouSureStop());
|
||||||
|
const yesno = options.addOptions("", {ltr: true});
|
||||||
|
yesno.addButtonInput("", I18n.yes(), () => {
|
||||||
|
fetch(this.info.api + "/stop", {headers: this.headers, method: "POST"});
|
||||||
|
menu.hide();
|
||||||
|
});
|
||||||
|
yesno.addButtonInput("", I18n.no(), () => {
|
||||||
|
menu.hide();
|
||||||
|
});
|
||||||
|
menu.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (this.rights.hasPermission("CREATE_REGISTRATION_TOKENS")) {
|
||||||
|
manageInstance.addButtonInput("", I18n.manageInstance.createTokens(), () => {
|
||||||
|
const tokens = manageInstance.addSubOptions(I18n.manageInstance.createTokens(), {
|
||||||
|
noSubmit: true,
|
||||||
|
});
|
||||||
|
const count = tokens.addTextInput(I18n.manageInstance.count(), () => {}, {
|
||||||
|
initText: "1",
|
||||||
|
});
|
||||||
|
const length = tokens.addTextInput(I18n.manageInstance.length(), () => {}, {
|
||||||
|
initText: "32",
|
||||||
|
});
|
||||||
|
const format = tokens.addSelect(
|
||||||
|
I18n.manageInstance.format(),
|
||||||
|
() => {},
|
||||||
|
[
|
||||||
|
I18n.manageInstance.TokenFormats.JSON(),
|
||||||
|
I18n.manageInstance.TokenFormats.plain(),
|
||||||
|
I18n.manageInstance.TokenFormats.URLs(),
|
||||||
|
],
|
||||||
|
{
|
||||||
|
defaultIndex: 2,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
format.watchForChange((e) => {
|
||||||
|
if (e !== 2) {
|
||||||
|
urlOption.removeAll();
|
||||||
|
} else {
|
||||||
|
makeURLMenu();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const urlOption = tokens.addOptions("");
|
||||||
|
const urlOptionsJSON = {
|
||||||
|
url: window.location.origin,
|
||||||
|
type: "Jank",
|
||||||
|
};
|
||||||
|
function makeURLMenu() {
|
||||||
|
urlOption
|
||||||
|
.addTextInput(I18n.manageInstance.clientURL(), () => {}, {
|
||||||
|
initText: urlOptionsJSON.url,
|
||||||
|
})
|
||||||
|
.watchForChange((str) => {
|
||||||
|
urlOptionsJSON.url = str;
|
||||||
|
});
|
||||||
|
urlOption
|
||||||
|
.addSelect(
|
||||||
|
I18n.manageInstance.regType(),
|
||||||
|
() => {},
|
||||||
|
["Jank", I18n.manageInstance.genericType()],
|
||||||
|
{
|
||||||
|
defaultIndex: ["Jank", "generic"].indexOf(urlOptionsJSON.type),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.watchForChange((i) => {
|
||||||
|
urlOptionsJSON.type = ["Jank", "generic"][i];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
makeURLMenu();
|
||||||
|
tokens.addButtonInput("", I18n.manageInstance.create(), async () => {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.set("count", count.value);
|
||||||
|
params.set("length", length.value);
|
||||||
|
const json = (await (
|
||||||
|
await fetch(
|
||||||
|
this.info.api + "/auth/generate-registration-tokens?" + params.toString(),
|
||||||
|
{
|
||||||
|
headers: this.headers,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
).json()) as {tokens: string[]};
|
||||||
|
if (format.index === 0) {
|
||||||
|
pre.textContent = JSON.stringify(json.tokens);
|
||||||
|
} else if (format.index === 1) {
|
||||||
|
pre.textContent = json.tokens.join("\n");
|
||||||
|
} else if (format.index === 2) {
|
||||||
|
if (urlOptionsJSON.type === "Jank") {
|
||||||
|
const options = new URLSearchParams();
|
||||||
|
options.set("instance", this.info.wellknown);
|
||||||
|
pre.textContent = json.tokens
|
||||||
|
.map((token) => {
|
||||||
|
options.set("token", token);
|
||||||
|
return `${urlOptionsJSON.url}/register?` + options.toString();
|
||||||
|
})
|
||||||
|
.join("\n");
|
||||||
|
} else {
|
||||||
|
const options = new URLSearchParams();
|
||||||
|
pre.textContent = json.tokens
|
||||||
|
.map((token) => {
|
||||||
|
options.set("token", token);
|
||||||
|
return `${urlOptionsJSON.url}/register?` + options.toString();
|
||||||
|
})
|
||||||
|
.join("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tokens.addButtonInput("", I18n.manageInstance.copy(), async () => {
|
||||||
|
try {
|
||||||
|
if (pre.textContent) {
|
||||||
|
await navigator.clipboard.writeText(pre.textContent);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const pre = document.createElement("pre");
|
||||||
|
tokens.addHTMLArea(pre);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
settings.show();
|
settings.show();
|
||||||
}
|
}
|
||||||
readonly botTokens: Map<string, string> = new Map();
|
readonly botTokens: Map<string, string> = new Map();
|
||||||
|
|
|
@ -88,7 +88,10 @@ if (instancein) {
|
||||||
}
|
}
|
||||||
timeout = setTimeout(() => checkInstance((instancein as HTMLInputElement).value), 1000);
|
timeout = setTimeout(() => checkInstance((instancein as HTMLInputElement).value), 1000);
|
||||||
});
|
});
|
||||||
if (localStorage.getItem("instanceinfo")) {
|
if (
|
||||||
|
localStorage.getItem("instanceinfo") &&
|
||||||
|
!new URLSearchParams(window.location.search).get("instance")
|
||||||
|
) {
|
||||||
const json = JSON.parse(localStorage.getItem("instanceinfo")!);
|
const json = JSON.parse(localStorage.getItem("instanceinfo")!);
|
||||||
if (json.value) {
|
if (json.value) {
|
||||||
(instancein as HTMLInputElement).value = json.value;
|
(instancein as HTMLInputElement).value = json.value;
|
||||||
|
|
|
@ -42,9 +42,13 @@ async function registertry(e: Event) {
|
||||||
|
|
||||||
const instanceInfo = JSON.parse(localStorage.getItem("instanceinfo") ?? "{}");
|
const instanceInfo = JSON.parse(localStorage.getItem("instanceinfo") ?? "{}");
|
||||||
const apiurl = new URL(instanceInfo.api);
|
const apiurl = new URL(instanceInfo.api);
|
||||||
|
let add = "";
|
||||||
|
const token = new URLSearchParams(window.location.search).get("token");
|
||||||
|
if (token) {
|
||||||
|
add = "?" + new URLSearchParams([["token", token]]).toString();
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const response = await fetch(apiurl + "/auth/register", {
|
const response = await fetch(apiurl + "/auth/register" + add, {
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
date_of_birth: dateofbirth,
|
date_of_birth: dateofbirth,
|
||||||
email,
|
email,
|
||||||
|
|
116
src/webpage/rights.ts
Normal file
116
src/webpage/rights.ts
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
import {I18n} from "./i18n.js";
|
||||||
|
|
||||||
|
class Rights {
|
||||||
|
allow!: bigint;
|
||||||
|
constructor(allow: string | number) {
|
||||||
|
this.update(allow);
|
||||||
|
}
|
||||||
|
update(allow: string | number) {
|
||||||
|
try {
|
||||||
|
this.allow = BigInt(allow);
|
||||||
|
} catch {
|
||||||
|
this.allow = 875069521787904n;
|
||||||
|
console.error(
|
||||||
|
`Something really stupid happened with a permission with allow being ${allow}, execution will still happen, but something really stupid happened, please report if you know what caused this.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getPermissionbit(b: number, big: bigint): boolean {
|
||||||
|
return Boolean((big >> BigInt(b)) & 1n);
|
||||||
|
}
|
||||||
|
setPermissionbit(b: number, state: boolean, big: bigint): bigint {
|
||||||
|
const bit = 1n << BigInt(b);
|
||||||
|
return (big & ~bit) | (BigInt(state) << BigInt(b)); //thanks to geotale for this code :3
|
||||||
|
}
|
||||||
|
static *info(): Generator<{name: string; readableName: string; description: string}> {
|
||||||
|
throw new Error("Isn't implemented");
|
||||||
|
for (const thing of this.permisions) {
|
||||||
|
yield {
|
||||||
|
name: thing,
|
||||||
|
readableName: I18n.getTranslation("permissions.readableNames." + thing),
|
||||||
|
description: I18n.getTranslation("permissions.descriptions." + thing),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static readonly permisions = [
|
||||||
|
"OPERATOR",
|
||||||
|
"MANAGE_APPLICATIONS",
|
||||||
|
"MANAGE_GUILDS",
|
||||||
|
"MANAGE_MESSAGES",
|
||||||
|
"MANAGE_RATE_LIMITS",
|
||||||
|
"MANAGE_ROUTING",
|
||||||
|
"MANAGE_TICKETS",
|
||||||
|
"MANAGE_USERS",
|
||||||
|
"ADD_MEMBERS",
|
||||||
|
"BYPASS_RATE_LIMITS",
|
||||||
|
"CREATE_APPLICATIONS",
|
||||||
|
"CREATE_CHANNELS",
|
||||||
|
"CREATE_DMS",
|
||||||
|
"CREATE_DM_GROUPS",
|
||||||
|
"CREATE_GUILDS",
|
||||||
|
"CREATE_INVITES",
|
||||||
|
"CREATE_ROLES",
|
||||||
|
"CREATE_TEMPLATES",
|
||||||
|
"CREATE_WEBHOOKS",
|
||||||
|
"JOIN_GUILDS",
|
||||||
|
"PIN_MESSAGES",
|
||||||
|
"SELF_ADD_REACTIONS",
|
||||||
|
"SELF_DELETE_MESSAGES",
|
||||||
|
"SELF_EDIT_MESSAGES",
|
||||||
|
"SELF_EDIT_NAME",
|
||||||
|
"SEND_MESSAGES",
|
||||||
|
"USE_ACTIVITIES",
|
||||||
|
"USE_VIDEO",
|
||||||
|
"USE_VOICE",
|
||||||
|
"INVITE_USERS",
|
||||||
|
"SELF_DELETE_DISABLE",
|
||||||
|
"DEBTABLE",
|
||||||
|
"CREDITABLE",
|
||||||
|
"KICK_BAN_MEMBERS",
|
||||||
|
"SELF_LEAVE_GROUPS",
|
||||||
|
"PRESENCE",
|
||||||
|
"SELF_ADD_DISCOVERABLE",
|
||||||
|
"MANAGE_GUILD_DIRECTORY",
|
||||||
|
"POGGERS",
|
||||||
|
"USE_ACHIEVEMENTS",
|
||||||
|
"INITIATE_INTERACTIONS",
|
||||||
|
"RESPOND_TO_INTERACTIONS",
|
||||||
|
"SEND_BACKDATED_EVENTS",
|
||||||
|
"USE_MASS_INVITES",
|
||||||
|
"ACCEPT_INVITES",
|
||||||
|
"SELF_EDIT_FLAGS",
|
||||||
|
"EDIT_FLAGS",
|
||||||
|
"MANAGE_GROUPS",
|
||||||
|
"VIEW_SERVER_STATS",
|
||||||
|
"RESEND_VERIFICATION_EMAIL",
|
||||||
|
"CREATE_REGISTRATION_TOKENS",
|
||||||
|
];
|
||||||
|
getPermission(name: string): boolean {
|
||||||
|
if (undefined === Rights.permisions.indexOf(name)) {
|
||||||
|
console.error(name + " is not found in map", Rights.permisions);
|
||||||
|
}
|
||||||
|
return this.getPermissionbit(Rights.permisions.indexOf(name), this.allow);
|
||||||
|
}
|
||||||
|
hasPermission(name: string, adminOverride = true): boolean {
|
||||||
|
if (this.getPermissionbit(Rights.permisions.indexOf(name), this.allow)) return true;
|
||||||
|
if (name !== "OPERATOR" && adminOverride) return this.hasPermission("OPERATOR");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
setPermission(name: string, setto: number): void {
|
||||||
|
const bit = Rights.permisions.indexOf(name);
|
||||||
|
if (bit === undefined) {
|
||||||
|
return console.error(
|
||||||
|
"Tried to set permission to " + setto + " for " + name + " but it doesn't exist",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setto === 0) {
|
||||||
|
this.allow = this.setPermissionbit(bit, false, this.allow);
|
||||||
|
} else if (setto === 1) {
|
||||||
|
this.allow = this.setPermissionbit(bit, true, this.allow);
|
||||||
|
} else {
|
||||||
|
console.error("invalid number entered:" + setto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export {Rights};
|
|
@ -4,6 +4,11 @@ import {Guild} from "./guild.js";
|
||||||
import {SnowFlake} from "./snowflake.js";
|
import {SnowFlake} from "./snowflake.js";
|
||||||
import {rolesjson} from "./jsontypes.js";
|
import {rolesjson} from "./jsontypes.js";
|
||||||
import {Search} from "./search.js";
|
import {Search} from "./search.js";
|
||||||
|
import {OptionsElement, Buttons} from "./settings.js";
|
||||||
|
import {Contextmenu} from "./contextmenu.js";
|
||||||
|
import {Channel} from "./channel.js";
|
||||||
|
import {I18n} from "./i18n.js";
|
||||||
|
|
||||||
class Role extends SnowFlake {
|
class Role extends SnowFlake {
|
||||||
permissions: Permissions;
|
permissions: Permissions;
|
||||||
owner: Guild;
|
owner: Guild;
|
||||||
|
@ -135,10 +140,7 @@ class PermissionToggle implements OptionsElement<number> {
|
||||||
}
|
}
|
||||||
submit() {}
|
submit() {}
|
||||||
}
|
}
|
||||||
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 {
|
class RoleList extends Buttons {
|
||||||
permissions: [Role, Permissions][];
|
permissions: [Role, Permissions][];
|
||||||
permission: Permissions;
|
permission: Permissions;
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {Search} from "./search.js";
|
||||||
import {I18n} from "./i18n.js";
|
import {I18n} from "./i18n.js";
|
||||||
import {Direct} from "./direct.js";
|
import {Direct} from "./direct.js";
|
||||||
import {Hover} from "./hover.js";
|
import {Hover} from "./hover.js";
|
||||||
|
import {Dialog} from "./settings.js";
|
||||||
|
|
||||||
class User extends SnowFlake {
|
class User extends SnowFlake {
|
||||||
owner: Localuser;
|
owner: Localuser;
|
||||||
|
@ -69,6 +70,10 @@ class User extends SnowFlake {
|
||||||
"totp_secret",
|
"totp_secret",
|
||||||
"webauthn_enabled",
|
"webauthn_enabled",
|
||||||
]);
|
]);
|
||||||
|
if (!this.localuser.rights.getPermission("OPERATOR")) {
|
||||||
|
//Unless the user is an operator, we really shouldn't ever see this
|
||||||
|
bad.add("rights");
|
||||||
|
}
|
||||||
for (const thing of bad) {
|
for (const thing of bad) {
|
||||||
if (json.hasOwnProperty(thing)) {
|
if (json.hasOwnProperty(thing)) {
|
||||||
console.error(thing + " should not be exposed to the client");
|
console.error(thing + " should not be exposed to the client");
|
||||||
|
@ -346,6 +351,36 @@ class User extends SnowFlake {
|
||||||
navigator.clipboard.writeText(this.id);
|
navigator.clipboard.writeText(this.id);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.contextmenu.addSeperator();
|
||||||
|
|
||||||
|
this.contextmenu.addButton(
|
||||||
|
() => I18n.user.instanceBan(),
|
||||||
|
function (this: User) {
|
||||||
|
const menu = new Dialog("");
|
||||||
|
const options = menu.float.options;
|
||||||
|
options.addTitle(I18n.user.confirmInstBan(this.name));
|
||||||
|
const opt = options.addOptions("", {ltr: true});
|
||||||
|
opt.addButtonInput("", I18n.yes(), () => {
|
||||||
|
fetch(this.info.api + "/users/" + this.id, {
|
||||||
|
headers: this.localuser.headers,
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
menu.hide();
|
||||||
|
});
|
||||||
|
opt.addButtonInput("", I18n.no(), () => {
|
||||||
|
menu.hide();
|
||||||
|
});
|
||||||
|
menu.show();
|
||||||
|
},
|
||||||
|
{
|
||||||
|
visable: function () {
|
||||||
|
return this.localuser.rights.hasPermission("MANAGE_USERS");
|
||||||
|
},
|
||||||
|
color: "red",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
console.warn("this ran");
|
||||||
}
|
}
|
||||||
|
|
||||||
static checkuser(user: User | userjson, owner: Localuser): User {
|
static checkuser(user: User | userjson, owner: Localuser): User {
|
||||||
|
@ -461,6 +496,14 @@ class User extends SnowFlake {
|
||||||
}
|
}
|
||||||
(this as any)[key] = (json as any)[key];
|
(this as any)[key] = (json as any)[key];
|
||||||
}
|
}
|
||||||
|
if ("rights" in this) {
|
||||||
|
if (
|
||||||
|
this === this.localuser.user &&
|
||||||
|
(typeof this.rights == "string" || typeof this.rights == "number")
|
||||||
|
) {
|
||||||
|
this.localuser.updateRights(this.rights);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bind(html: HTMLElement, guild: Guild | null = null, error = true): void {
|
bind(html: HTMLElement, guild: Guild | null = null, error = true): void {
|
||||||
|
|
|
@ -321,7 +321,26 @@
|
||||||
"areYouSureDelete": "Are you sure you want to delete your account? If so enter the phrase $1",
|
"areYouSureDelete": "Are you sure you want to delete your account? If so enter the phrase $1",
|
||||||
"sillyDeleteConfirmPhrase": "Shrek is love, Shrek is life",
|
"sillyDeleteConfirmPhrase": "Shrek is love, Shrek is life",
|
||||||
"deleteAccountButton": "Delete account",
|
"deleteAccountButton": "Delete account",
|
||||||
"mustTypePhrase": "To delete your account you must type the phrase"
|
"mustTypePhrase": "To delete your account you must type the phrase",
|
||||||
|
"manageInstance": "Manage Instance"
|
||||||
|
},
|
||||||
|
"manageInstance": {
|
||||||
|
"stop": "Stop instance",
|
||||||
|
"AreYouSureStop": "Are you sure you want to stop this instance?",
|
||||||
|
"createTokens": "Create Registration Tokens",
|
||||||
|
"count": "Count:",
|
||||||
|
"length": "Length:",
|
||||||
|
"format": "Format:",
|
||||||
|
"TokenFormats": {
|
||||||
|
"plain": "Plain",
|
||||||
|
"JSON": "JSON formatted",
|
||||||
|
"URLs": "Invite URLs"
|
||||||
|
},
|
||||||
|
"create": "Create",
|
||||||
|
"clientURL": "Client URL:",
|
||||||
|
"regType": "Register token URL type",
|
||||||
|
"genericType": "Generic",
|
||||||
|
"copy": "Copy"
|
||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"reactionAdd": "Add reaction",
|
"reactionAdd": "Add reaction",
|
||||||
|
@ -403,7 +422,9 @@
|
||||||
"ban": "Ban member",
|
"ban": "Ban member",
|
||||||
"addRole": "Add roles",
|
"addRole": "Add roles",
|
||||||
"removeRole": "Remove roles",
|
"removeRole": "Remove roles",
|
||||||
"editServerProfile": "Edit server profile"
|
"editServerProfile": "Edit server profile",
|
||||||
|
"instanceBan": "Instance ban",
|
||||||
|
"confirmInstBan": "Are you sure you want to instance ban $1?"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"checking": "Checking Instance",
|
"checking": "Checking Instance",
|
||||||
|
@ -453,6 +474,8 @@
|
||||||
"name:": "Name:",
|
"name:": "Name:",
|
||||||
"confirmDel": "Are you sure you want to delete this emoji?"
|
"confirmDel": "Are you sure you want to delete this emoji?"
|
||||||
},
|
},
|
||||||
|
"incorrectURLS": "## This instance has likely sent the incorrect URLs.\n### If you're the instance owner please see [here](https://docs.spacebar.chat/setup/server/) under *Connecting from remote machines* to correct the issue.\n Would you like Jank Client to automatically try to fix this error to let you connect to the instance?",
|
||||||
|
|
||||||
"uploadFilesText": "Upload your files here!",
|
"uploadFilesText": "Upload your files here!",
|
||||||
"errorReconnect": "Unable to connect to the server, retrying in **$1** seconds...",
|
"errorReconnect": "Unable to connect to the server, retrying in **$1** seconds...",
|
||||||
"retrying": "Retrying...",
|
"retrying": "Retrying...",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue