marge inital template support
This commit is contained in:
parent
51902f5307
commit
8bc009dc19
10 changed files with 359 additions and 3 deletions
|
@ -177,6 +177,10 @@ app.use("/", async (req: Request, res: Response) => {
|
|||
res.sendFile(path.join(__dirname, "webpage", "invite.html"));
|
||||
return;
|
||||
}
|
||||
if (req.path.startsWith("/template/")) {
|
||||
res.sendFile(path.join(__dirname, "webpage", "template.html"));
|
||||
return;
|
||||
}
|
||||
const filePath = await combinePath("/webpage/" + req.path);
|
||||
res.sendFile(filePath);
|
||||
});
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
extendedProperties,
|
||||
banObj,
|
||||
addInfoBan,
|
||||
templateSkim,
|
||||
} from "./jsontypes.js";
|
||||
import {User} from "./user.js";
|
||||
import {I18n} from "./i18n.js";
|
||||
|
@ -24,6 +25,7 @@ import {webhookMenu} from "./webhooks.js";
|
|||
import {createImg} from "./utils/utils.js";
|
||||
import {Sticker} from "./sticker.js";
|
||||
import {ProgessiveDecodeJSON} from "./utils/progessiveLoad.js";
|
||||
import {getApiUrls} from "../utils.js";
|
||||
|
||||
class Guild extends SnowFlake {
|
||||
owner!: Localuser;
|
||||
|
@ -581,7 +583,89 @@ class Guild extends SnowFlake {
|
|||
})();
|
||||
const webhooks = settings.addButton(I18n.webhooks.base());
|
||||
webhookMenu(this, this.info.api + `/guilds/${this.id}/webhooks`, webhooks);
|
||||
console.log(this.properties.features, this.properties.features.includes("COMMUNITY"));
|
||||
const template = settings.addButton(I18n.guild.templates());
|
||||
(async () => {
|
||||
template.addText(I18n.guild.templcateMetaDesc());
|
||||
const generateTemplateArea = (temp: templateSkim) => {
|
||||
const div = document.createElement("div");
|
||||
div.classList.add("flexltr", "templateMiniBox");
|
||||
const code = document.createElement("span");
|
||||
|
||||
code.textContent = temp.code + ` (${temp.name})`;
|
||||
|
||||
const edit = document.createElement("button");
|
||||
edit.textContent = I18n.edit();
|
||||
edit.onclick = () => {
|
||||
const form = template.addSubForm(
|
||||
I18n.guild.editingTemplate(temp.name),
|
||||
(tempy) => {
|
||||
const template = tempy as templateSkim;
|
||||
temp.name = template.name;
|
||||
temp.description = template.description;
|
||||
},
|
||||
{
|
||||
fetchURL: this.info.api + "/guilds/" + this.id + "/templates/" + temp.code,
|
||||
method: "PATCH",
|
||||
headers: this.headers,
|
||||
},
|
||||
);
|
||||
const search = new URLSearchParams([["instance", this.info.wellknown]]);
|
||||
form.addMDText(
|
||||
I18n.guild.templateURL(
|
||||
window.location.origin + "/template/" + temp.code + "?" + search,
|
||||
),
|
||||
);
|
||||
|
||||
const name = form.addTextInput(I18n.guild.templateName(), "name", {
|
||||
initText: temp.name,
|
||||
});
|
||||
form.addMDInput(I18n.guild.templateDesc(), "description", {
|
||||
initText: temp.description,
|
||||
});
|
||||
User.resolve(temp.creator_id, this.localuser).then((_) => {
|
||||
form.addText(I18n.guild.tempCreatedBy());
|
||||
form.addHTMLArea(_.createWidget(this));
|
||||
});
|
||||
form.addText(I18n.guild.tempUseCount((temp.usage_count || 0) + ""));
|
||||
form.addPreprocessor(() => {
|
||||
if (name.value.length < 2) {
|
||||
throw new FormError(name, I18n.guild.templateNameShort());
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
div.append(code, edit);
|
||||
template.addHTMLArea(div);
|
||||
};
|
||||
template.addButtonInput("", I18n.guild.createNewTemplate(), () => {
|
||||
const form = template.addSubForm(
|
||||
I18n.guild.createNewTemplate(),
|
||||
(code) => {
|
||||
template.returnFromSub();
|
||||
generateTemplateArea(code as templateSkim);
|
||||
},
|
||||
{
|
||||
fetchURL: this.info.api + "/guilds/" + this.id + "/templates",
|
||||
method: "POST",
|
||||
headers: this.headers,
|
||||
},
|
||||
);
|
||||
form.addText(I18n.guild.templcateMetaDesc());
|
||||
const name = form.addTextInput(I18n.guild.templateName(), "name");
|
||||
form.addMDInput(I18n.guild.templateDesc(), "description");
|
||||
form.addPreprocessor(() => {
|
||||
if (name.value.length < 2) {
|
||||
throw new FormError(name, I18n.guild.templateNameShort());
|
||||
}
|
||||
});
|
||||
});
|
||||
const templates = (await (
|
||||
await fetch(this.info.api + "/guilds/" + this.id + "/templates", {headers: this.headers})
|
||||
).json()) as templateSkim[];
|
||||
for (const temp of templates.reverse()) {
|
||||
generateTemplateArea(temp);
|
||||
}
|
||||
})();
|
||||
let com = false;
|
||||
if (this.properties.features.includes("COMMUNITY")) {
|
||||
this.addCommunity(settings, textChannels);
|
||||
|
|
|
@ -7,6 +7,7 @@ import {Message} from "./message.js";
|
|||
import {File} from "./file.js";
|
||||
import {I18n} from "./i18n.js";
|
||||
(async () => {
|
||||
let templateID = new URLSearchParams(window.location.search).get("templateID");
|
||||
await I18n.done;
|
||||
|
||||
if (!Localuser.users.currentuser) {
|
||||
|
@ -54,6 +55,9 @@ import {I18n} from "./i18n.js";
|
|||
loading.classList.add("doneloading");
|
||||
loading.classList.remove("loading");
|
||||
console.log("done loading");
|
||||
if (templateID) {
|
||||
thisUser.passTemplateID(templateID);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
|
|
@ -131,6 +131,37 @@ interface banObj {
|
|||
public_flags: number;
|
||||
};
|
||||
}
|
||||
interface templateSkim {
|
||||
id: string;
|
||||
code: string;
|
||||
name: string;
|
||||
description: string;
|
||||
usage_count: null | number;
|
||||
creator_id: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
source_guild_id: string;
|
||||
serialized_source_guild: {
|
||||
id: string;
|
||||
afk_channel_id: null | string;
|
||||
afk_timeout: number;
|
||||
default_message_notifications: number;
|
||||
description: null | "string";
|
||||
explicit_content_filter: number;
|
||||
features: string[];
|
||||
icon: null | string;
|
||||
large: boolean;
|
||||
name: string;
|
||||
preferred_locale: string;
|
||||
region: string;
|
||||
system_channel_id: null | string;
|
||||
system_channel_flags: number;
|
||||
verification_level: number;
|
||||
widget_enabled: boolean;
|
||||
nsfw: boolean;
|
||||
premium_progress_bar_enabled: boolean;
|
||||
};
|
||||
}
|
||||
interface addInfoBan {
|
||||
id: string;
|
||||
user_id: string;
|
||||
|
@ -838,4 +869,5 @@ export {
|
|||
stickerJson,
|
||||
banObj,
|
||||
addInfoBan,
|
||||
templateSkim,
|
||||
};
|
||||
|
|
|
@ -1046,6 +1046,7 @@ class Localuser {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
loadGuild(id: string, forceReload = false): Guild | undefined {
|
||||
this.searching = false;
|
||||
let guild = this.guildids.get(id);
|
||||
|
@ -1172,7 +1173,10 @@ class Localuser {
|
|||
}
|
||||
this.unreads();
|
||||
}
|
||||
createGuild() {
|
||||
passTemplateID(id: string) {
|
||||
this.createGuild(id);
|
||||
}
|
||||
createGuild(templateID?: string) {
|
||||
const full = new Dialog("");
|
||||
const buttons = full.options.addButtons("", {top: true});
|
||||
const viacode = buttons.add(I18n.getTranslation("invite.joinUsing"));
|
||||
|
@ -1224,7 +1228,61 @@ class Localuser {
|
|||
full.hide();
|
||||
});
|
||||
}
|
||||
const guildcreateFromTemplate = buttons.add(I18n.guild.createFromTemplate());
|
||||
{
|
||||
const form = guildcreateFromTemplate.addForm(
|
||||
"",
|
||||
(_: any) => {
|
||||
if (_.message) {
|
||||
loading.hide();
|
||||
full.show();
|
||||
alert(_.message);
|
||||
const htmlarea = buttons.htmlarea.deref();
|
||||
if (htmlarea) buttons.generateHTMLArea(guildcreateFromTemplate, htmlarea);
|
||||
} else {
|
||||
loading.hide();
|
||||
full.hide();
|
||||
}
|
||||
},
|
||||
{
|
||||
method: "POST",
|
||||
headers: this.headers,
|
||||
},
|
||||
);
|
||||
const template = form.addTextInput(I18n.guild.template(), "template", {
|
||||
initText: templateID || "",
|
||||
});
|
||||
form.addFileInput(I18n.getTranslation("guild.icon:"), "icon", {files: "one"});
|
||||
form.addTextInput(I18n.getTranslation("guild.name:"), "name", {required: true});
|
||||
|
||||
const loading = new Dialog("");
|
||||
loading.float.options.addTitle(I18n.guild.creating());
|
||||
form.onFormError = () => {
|
||||
loading.hide();
|
||||
full.show();
|
||||
};
|
||||
form.addPreprocessor((e) => {
|
||||
loading.show();
|
||||
full.hide();
|
||||
if ("template" in e) delete e.template;
|
||||
let code: string;
|
||||
if (URL.canParse(template.value)) {
|
||||
const url = new URL(template.value);
|
||||
code = url.pathname.split("/").at(-1) as string;
|
||||
if (url.host === "discord.com") {
|
||||
code = "discord:" + code;
|
||||
}
|
||||
} else {
|
||||
code = template.value;
|
||||
}
|
||||
form.fetchURL = this.info.api + "/guilds/templates/" + code;
|
||||
});
|
||||
}
|
||||
full.show();
|
||||
if (templateID) {
|
||||
const htmlarea = buttons.htmlarea.deref();
|
||||
if (htmlarea) buttons.generateHTMLArea(guildcreateFromTemplate, htmlarea);
|
||||
}
|
||||
}
|
||||
async makeGuild(fields: {name: string; icon: string | null}) {
|
||||
return await (
|
||||
|
|
|
@ -1095,7 +1095,7 @@ class Form implements OptionsElement<object> {
|
|||
readonly names: Map<string, OptionsElement<any>> = new Map();
|
||||
readonly required: WeakSet<OptionsElement<any>> = new WeakSet();
|
||||
readonly submitText: string;
|
||||
readonly fetchURL: string;
|
||||
fetchURL: string;
|
||||
readonly headers = {};
|
||||
readonly method: string;
|
||||
value!: object;
|
||||
|
|
|
@ -59,6 +59,18 @@ body {
|
|||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
.templateMiniBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 6px;
|
||||
border-radius: 4px;
|
||||
width: fit-content;
|
||||
background: var(--secondary-bg);
|
||||
|
||||
button {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
.flexltr {
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
|
@ -1952,6 +1964,7 @@ img.bigembedimg {
|
|||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
align-items: flex-start;
|
||||
z-index: 3;
|
||||
}
|
||||
.hypoprofile {
|
||||
position: relative;
|
||||
|
|
35
src/webpage/template.html
Normal file
35
src/webpage/template.html
Normal file
|
@ -0,0 +1,35 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Jank Client</title>
|
||||
<meta content="Invite" property="og:title" />
|
||||
<meta content="Accept this invite for a spacebar guild" property="og:description" />
|
||||
<meta name="description" content="A server template" />
|
||||
<meta content="/logo.webp" property="og:image" />
|
||||
<meta content="#4b458c" data-react-helmet="true" name="theme-color" />
|
||||
<link href="/style.css" rel="stylesheet" />
|
||||
<link href="/themes.css" rel="stylesheet" id="lightcss" />
|
||||
<style>
|
||||
body.no-theme {
|
||||
background: #16191b;
|
||||
}
|
||||
@media (prefers-color-scheme: light) {
|
||||
body.no-theme {
|
||||
background: #9397bd;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="no-theme">
|
||||
<div>
|
||||
<div id="invitebody">
|
||||
<h1 id="templatename">Use Template Name</h1>
|
||||
<p id="templatedescription"></p>
|
||||
<button id="usetemplate">Use template</button>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/templatePage.js"></script>
|
||||
</body>
|
||||
</html>
|
110
src/webpage/templatePage.ts
Normal file
110
src/webpage/templatePage.ts
Normal file
|
@ -0,0 +1,110 @@
|
|||
import {I18n} from "./i18n.js";
|
||||
import {templateSkim} from "./jsontypes.js";
|
||||
import {getapiurls} from "./utils/utils.js";
|
||||
import {getBulkUsers, Specialuser} from "./utils/utils.js";
|
||||
|
||||
(async () => {
|
||||
const users = getBulkUsers();
|
||||
const well = new URLSearchParams(window.location.search).get("instance");
|
||||
const joinable: Specialuser[] = [];
|
||||
|
||||
for (const key in users.users) {
|
||||
if (Object.prototype.hasOwnProperty.call(users.users, key)) {
|
||||
const user: Specialuser = users.users[key];
|
||||
if (well && user.serverurls.wellknown.includes(well)) {
|
||||
joinable.push(user);
|
||||
}
|
||||
console.log(user);
|
||||
}
|
||||
}
|
||||
|
||||
let urls: {api: string; cdn: string} | undefined;
|
||||
|
||||
if (!joinable.length && well) {
|
||||
const out = await getapiurls(well);
|
||||
if (out) {
|
||||
urls = out;
|
||||
for (const key in users.users) {
|
||||
if (Object.prototype.hasOwnProperty.call(users.users, key)) {
|
||||
const user: Specialuser = users.users[key];
|
||||
if (user.serverurls.api.includes(out.api)) {
|
||||
joinable.push(user);
|
||||
}
|
||||
console.log(user);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new Error("Someone needs to handle the case where the servers don't exist");
|
||||
}
|
||||
} else {
|
||||
urls = joinable[0].serverurls;
|
||||
}
|
||||
await I18n.done;
|
||||
if (!joinable.length) {
|
||||
document.getElementById("usetemplate")!.textContent = I18n.htmlPages.noAccount();
|
||||
}
|
||||
|
||||
const code = window.location.pathname.split("/")[2];
|
||||
|
||||
fetch(`${urls!.api}/guilds/templates/${code}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: joinable[0].token,
|
||||
},
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((json) => {
|
||||
const template = json as templateSkim;
|
||||
document.getElementById("templatename")!.textContent = I18n.useTemplate(template.name);
|
||||
document.getElementById("templatedescription")!.textContent = template.description;
|
||||
});
|
||||
|
||||
function showAccounts(): void {
|
||||
const table = document.createElement("dialog");
|
||||
for (const user of joinable) {
|
||||
console.log(user.pfpsrc);
|
||||
|
||||
const userinfo = document.createElement("div");
|
||||
userinfo.classList.add("flexltr", "switchtable");
|
||||
|
||||
const pfp = document.createElement("img");
|
||||
pfp.src = user.pfpsrc;
|
||||
pfp.classList.add("pfp");
|
||||
userinfo.append(pfp);
|
||||
|
||||
const userDiv = document.createElement("div");
|
||||
userDiv.classList.add("userinfo");
|
||||
userDiv.textContent = user.username;
|
||||
userDiv.append(document.createElement("br"));
|
||||
|
||||
const span = document.createElement("span");
|
||||
span.textContent = user.serverurls.wellknown.replace("https://", "").replace("http://", "");
|
||||
span.classList.add("serverURL");
|
||||
userDiv.append(span);
|
||||
|
||||
userinfo.append(userDiv);
|
||||
table.append(userinfo);
|
||||
|
||||
userinfo.addEventListener("click", () => {
|
||||
const search = new URLSearchParams();
|
||||
search.set("templateID", code);
|
||||
sessionStorage.setItem("currentuser", user.uid);
|
||||
window.location.assign("/channels/@me?" + search);
|
||||
});
|
||||
}
|
||||
|
||||
if (!joinable.length) {
|
||||
const l = new URLSearchParams("?");
|
||||
l.set("goback", window.location.href);
|
||||
l.set("instance", well!);
|
||||
window.location.href = "/login?" + l.toString();
|
||||
}
|
||||
|
||||
table.classList.add("flexttb", "accountSwitcher");
|
||||
console.log(table);
|
||||
document.body.append(table);
|
||||
}
|
||||
|
||||
document.getElementById("usetemplate")!.addEventListener("click", showAccounts);
|
||||
document.getElementById("usetemplate")!.textContent = I18n.useTemplateButton();
|
||||
})();
|
|
@ -230,6 +230,8 @@
|
|||
"box3title": "Contribute to Jank Client",
|
||||
"box3description": "We always appreciate some help, whether that be in the form of bug reports, or code, or even just pointing out some typos."
|
||||
},
|
||||
"useTemplate": "Use $1 as a template",
|
||||
"useTemplateButton": "Use Template",
|
||||
"register": {
|
||||
"passwordError:": "Password: $1",
|
||||
"usernameError": "Username: $1",
|
||||
|
@ -244,7 +246,21 @@
|
|||
"goThereTrust": "Go there and trust in the future",
|
||||
"nevermind": "Nevermind",
|
||||
"submit": "submit",
|
||||
"edit": "Edit",
|
||||
"guild": {
|
||||
"template": "Template:",
|
||||
"viewTemplate": "View Template",
|
||||
"createFromTemplate": "Create From Template",
|
||||
"tempUseCount": "Template has been used $1 {{PLURAL:$1|time|times}}",
|
||||
"tempCreatedBy": "Template created by:",
|
||||
"editingTemplate": "Editing $1",
|
||||
"createNewTemplate": "Create New Template",
|
||||
"templates": "Templates",
|
||||
"templateName": "Template Name:",
|
||||
"templateDesc": "Template Description:",
|
||||
"templcateMetaDesc": "A template allows others to use this guild as a base for their own guilds, it'll copy the channels, roles, and settings of this guild, but not the messages from within the guild, the bots, or the guilds icon.",
|
||||
"templateNameShort": "Template name must at least be 2 characters long",
|
||||
"templateURL": "Template URL: $1",
|
||||
"bannedBy": "Banned by:",
|
||||
"banReason": "Ban reason: $1",
|
||||
"bans": "Bans",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue