create guild settings for stickers
This commit is contained in:
parent
a69b8c552f
commit
956016a9a0
8 changed files with 363 additions and 3 deletions
|
@ -121,6 +121,27 @@ class Emoji {
|
|||
Emoji.decodeEmojiList(e);
|
||||
});
|
||||
}
|
||||
static getEmojiFromIDOrString(idOrString: string, localuser: Localuser) {
|
||||
for (const list of Emoji.emojis) {
|
||||
const emj = list.emojis.find((_) => _.emoji === idOrString);
|
||||
if (emj) {
|
||||
return new Emoji(emj, localuser);
|
||||
}
|
||||
}
|
||||
for (const guild of localuser.guilds) {
|
||||
const emj = guild.emojis.find((_) => _.id === idOrString);
|
||||
if (emj) {
|
||||
return new Emoji(emj, localuser);
|
||||
}
|
||||
}
|
||||
return new Emoji(
|
||||
{
|
||||
id: idOrString,
|
||||
name: "",
|
||||
},
|
||||
localuser,
|
||||
);
|
||||
}
|
||||
static async emojiPicker(
|
||||
this: typeof Emoji,
|
||||
x: number,
|
||||
|
|
|
@ -3,7 +3,7 @@ import {Localuser} from "./localuser.js";
|
|||
import {Contextmenu} from "./contextmenu.js";
|
||||
import {Role, RoleList} from "./role.js";
|
||||
import {Member} from "./member.js";
|
||||
import {Dialog, Options, Settings} from "./settings.js";
|
||||
import {Dialog, FormError, Options, Settings} from "./settings.js";
|
||||
import {Permissions} from "./permissions.js";
|
||||
import {SnowFlake} from "./snowflake.js";
|
||||
import {
|
||||
|
@ -20,6 +20,7 @@ import {I18n} from "./i18n.js";
|
|||
import {Emoji} from "./emoji.js";
|
||||
import {webhookMenu} from "./webhooks.js";
|
||||
import {createImg} from "./utils/utils.js";
|
||||
import {Sticker} from "./sticker.js";
|
||||
|
||||
class Guild extends SnowFlake {
|
||||
owner!: Localuser;
|
||||
|
@ -39,6 +40,7 @@ class Guild extends SnowFlake {
|
|||
html!: HTMLElement;
|
||||
emojis!: emojipjson[];
|
||||
large!: boolean;
|
||||
stickers!: Sticker[];
|
||||
members = new Set<Member>();
|
||||
static contextmenu = new Contextmenu<Guild, undefined>("guild menu");
|
||||
static setupcontextmenu() {
|
||||
|
@ -311,6 +313,117 @@ class Guild extends SnowFlake {
|
|||
genDiv();
|
||||
emoji.addHTMLArea(containdiv);
|
||||
}
|
||||
{
|
||||
const emoji = settings.addButton(I18n.sticker.title());
|
||||
emoji.addButtonInput("", I18n.sticker.upload(), () => {
|
||||
const popup = new Dialog(I18n.sticker.upload());
|
||||
const form = popup.options.addForm("", async () => {
|
||||
const body = new FormData();
|
||||
body.set("name", name.value);
|
||||
if (!filei.value) throw new FormError(filei, I18n.sticker.errFileMust());
|
||||
const file = filei.value.item(0);
|
||||
if (!file) throw new FormError(filei, I18n.sticker.errFileMust());
|
||||
body.set("file", file);
|
||||
if (!tags.value) throw new FormError(tags, I18n.sticker.errEmjMust());
|
||||
if (tags.value.id) {
|
||||
body.set("tags", tags.value.id);
|
||||
} else if (tags.value.emoji) {
|
||||
body.set("tags", tags.value.emoji);
|
||||
} else {
|
||||
throw new FormError(tags, I18n.sticker.errEmjMust());
|
||||
}
|
||||
const res = await fetch(this.info.api + "/guilds/" + this.id + "/stickers", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: this.headers.Authorization,
|
||||
},
|
||||
body,
|
||||
});
|
||||
if (res.ok) {
|
||||
popup.hide();
|
||||
} else {
|
||||
const json = await res.json();
|
||||
if ("message" in json && typeof json.message === "string") {
|
||||
throw new FormError(filei, json.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
const filei = form.addFileInput(I18n.sticker.image(), "file", {required: true});
|
||||
const name = form.addTextInput(I18n.sticker.name(), "name", {required: true});
|
||||
const tags = form.addEmojiInput(I18n.sticker.tags(), "tags", this.localuser, {
|
||||
required: true,
|
||||
});
|
||||
popup.show();
|
||||
});
|
||||
const containdiv = document.createElement("div");
|
||||
containdiv.classList.add("stickersDiv");
|
||||
const genDiv = () => {
|
||||
containdiv.innerHTML = "";
|
||||
for (const sticker of this.stickers) {
|
||||
const div = document.createElement("div");
|
||||
div.classList.add("flexttb", "stickerOption");
|
||||
|
||||
const text = document.createElement("span");
|
||||
text.textContent = sticker.name;
|
||||
|
||||
div.onclick = () => {
|
||||
const form = emoji.addSubForm(emoji.name, () => {}, {
|
||||
fetchURL: this.info.api + "/guilds/" + this.id + "/stickers/" + sticker.id,
|
||||
method: "PATCH",
|
||||
headers: this.headers,
|
||||
traditionalSubmit: true,
|
||||
});
|
||||
|
||||
form.addHTMLArea(sticker.getHTML());
|
||||
form.addTextInput(I18n.sticker.name(), "name", {
|
||||
initText: sticker.name,
|
||||
});
|
||||
|
||||
form.addMDInput(I18n.sticker.desc(), "description", {
|
||||
initText: sticker.description,
|
||||
});
|
||||
|
||||
let initEmoji = Emoji.getEmojiFromIDOrString(sticker.tags, this.localuser);
|
||||
form.addEmojiInput(I18n.sticker.tags(), "tags", this.localuser, {
|
||||
initEmoji,
|
||||
required: false,
|
||||
});
|
||||
|
||||
form.addButtonInput("", I18n.sticker.del(), () => {
|
||||
const diaolog = new Dialog("");
|
||||
diaolog.options.addTitle(I18n.sticker.confirmDel());
|
||||
const options = diaolog.options.addOptions("", {ltr: true});
|
||||
options.addButtonInput("", I18n.yes(), () => {
|
||||
fetch(`${this.info.api}/guilds/${this.id}/stickers/${sticker.id}`, {
|
||||
method: "DELETE",
|
||||
headers: this.headers,
|
||||
});
|
||||
diaolog.hide();
|
||||
});
|
||||
options.addButtonInput("", I18n.no(), () => {
|
||||
diaolog.hide();
|
||||
});
|
||||
diaolog.show();
|
||||
});
|
||||
};
|
||||
|
||||
div.append(sticker.getHTML(), text);
|
||||
|
||||
containdiv.append(div);
|
||||
}
|
||||
};
|
||||
this.onStickerUpdate = () => {
|
||||
emoji.returnFromSub();
|
||||
if (!document.body.contains(containdiv)) {
|
||||
this.onStickerUpdate = () => {};
|
||||
return;
|
||||
}
|
||||
genDiv();
|
||||
};
|
||||
genDiv();
|
||||
emoji.addHTMLArea(containdiv);
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const widgetMenu = settings.addButton(I18n.widget());
|
||||
const cur = (await (
|
||||
|
@ -349,6 +462,7 @@ class Guild extends SnowFlake {
|
|||
}
|
||||
settings.show();
|
||||
}
|
||||
onStickerUpdate = (_stickers: Sticker[]) => {};
|
||||
addCommunity(settings: Settings, textChannels: Channel[]) {
|
||||
const com = settings.addButton(I18n.guild.community()).addForm("", () => {}, {
|
||||
fetchURL: this.info.api + "/guilds/" + this.id,
|
||||
|
@ -606,6 +720,7 @@ class Guild extends SnowFlake {
|
|||
}
|
||||
}
|
||||
this.prevchannel = this.localuser.channelids.get(this.perminfo.prevchannel);
|
||||
this.stickers = json.stickers.map((_) => new Sticker(_, this));
|
||||
}
|
||||
get perminfo() {
|
||||
return this.localuser.perminfo.guilds[this.id];
|
||||
|
|
|
@ -237,12 +237,20 @@ type guildjson = {
|
|||
};
|
||||
roles: rolesjson[];
|
||||
stage_instances: [];
|
||||
stickers: [];
|
||||
stickers: stickerJson[];
|
||||
threads: [];
|
||||
version: string;
|
||||
guild_hashes: {};
|
||||
joined_at: string;
|
||||
};
|
||||
interface stickerJson {
|
||||
id: string;
|
||||
name: string;
|
||||
tags: string;
|
||||
type: number;
|
||||
format_type: number;
|
||||
description?: string;
|
||||
}
|
||||
type extendedProperties = guildjson["properties"] & {
|
||||
emojis: emojipjson[];
|
||||
large: boolean;
|
||||
|
@ -626,6 +634,15 @@ type wsjson =
|
|||
guild_id: string;
|
||||
};
|
||||
s: number;
|
||||
}
|
||||
| {
|
||||
op: 0;
|
||||
t: "GUILD_STICKERS_UPDATE";
|
||||
d: {
|
||||
guild_id: string;
|
||||
stickers: stickerJson[];
|
||||
};
|
||||
s: 3;
|
||||
};
|
||||
|
||||
type memberChunk = {
|
||||
|
@ -799,4 +816,5 @@ export {
|
|||
extendedProperties,
|
||||
webhookInfo,
|
||||
webhookType,
|
||||
stickerJson,
|
||||
};
|
||||
|
|
|
@ -31,6 +31,7 @@ import {Message} from "./message.js";
|
|||
import {badgeArr} from "./Dbadges.js";
|
||||
import {Rights} from "./rights.js";
|
||||
import {Contextmenu} from "./contextmenu.js";
|
||||
import {Sticker} from "./sticker.js";
|
||||
|
||||
const wsCodesRetry = new Set([4000, 4001, 4002, 4003, 4005, 4007, 4008, 4009]);
|
||||
interface CustomHTMLDivElement extends HTMLDivElement {
|
||||
|
@ -804,6 +805,13 @@ class Localuser {
|
|||
guild.onEmojiUpdate(guild.emojis);
|
||||
break;
|
||||
}
|
||||
case "GUILD_STICKERS_UPDATE": {
|
||||
const guild = this.guildids.get(temp.d.guild_id);
|
||||
if (!guild) break;
|
||||
guild.stickers = temp.d.stickers.map((_) => new Sticker(_, guild));
|
||||
guild.onStickerUpdate(guild.stickers);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
//@ts-ignore
|
||||
console.warn("Unhandled case " + temp.t, temp);
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import {Emoji} from "./emoji.js";
|
||||
import {I18n} from "./i18n.js";
|
||||
import {Localuser} from "./localuser.js";
|
||||
|
||||
interface OptionsElement<x> {
|
||||
//
|
||||
|
@ -521,6 +523,66 @@ class MDInput implements OptionsElement<string> {
|
|||
this.onSubmit(this.value);
|
||||
}
|
||||
}
|
||||
class EmojiInput implements OptionsElement<Emoji | undefined> {
|
||||
readonly label: string;
|
||||
readonly owner: Options;
|
||||
readonly onSubmit: (str: Emoji | undefined) => void;
|
||||
input!: WeakRef<HTMLInputElement>;
|
||||
value!: Emoji | undefined;
|
||||
localuser: Localuser;
|
||||
constructor(
|
||||
label: string,
|
||||
onSubmit: (str: Emoji | undefined) => void,
|
||||
owner: Options,
|
||||
localuser: Localuser,
|
||||
{initEmoji = undefined}: {initEmoji: undefined | Emoji},
|
||||
) {
|
||||
this.label = label;
|
||||
this.owner = owner;
|
||||
this.onSubmit = onSubmit;
|
||||
this.value = initEmoji;
|
||||
this.localuser = localuser;
|
||||
}
|
||||
generateHTML(): HTMLElement {
|
||||
const div = document.createElement("div");
|
||||
div.classList.add("flexltr", "emojiForm");
|
||||
const label = document.createElement("span");
|
||||
label.textContent = this.label;
|
||||
|
||||
let emoji: HTMLElement;
|
||||
if (this.value) {
|
||||
emoji = this.value.getHTML();
|
||||
} else {
|
||||
emoji = document.createElement("span");
|
||||
emoji.classList.add("emptyEmoji");
|
||||
}
|
||||
div.onclick = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
(async () => {
|
||||
const Emoji = (await import("./emoji.js")).Emoji;
|
||||
const emj = await Emoji.emojiPicker(e.x, e.y, this.localuser);
|
||||
if (emj) {
|
||||
this.value = emj;
|
||||
emoji.remove();
|
||||
emoji = emj.getHTML();
|
||||
div.append(emoji);
|
||||
this.onchange(emj);
|
||||
this.owner.changed();
|
||||
}
|
||||
})();
|
||||
};
|
||||
div.append(label, emoji);
|
||||
return div;
|
||||
}
|
||||
onchange = (_: Emoji | undefined) => {};
|
||||
watchForChange(func: (arg1: Emoji | undefined) => void) {
|
||||
this.onchange = func;
|
||||
}
|
||||
submit() {
|
||||
this.onSubmit(this.value);
|
||||
}
|
||||
}
|
||||
class FileInput implements OptionsElement<FileList | null> {
|
||||
readonly label: string;
|
||||
readonly owner: Options;
|
||||
|
@ -654,6 +716,7 @@ class Dialog {
|
|||
background.remove();
|
||||
}
|
||||
}
|
||||
|
||||
export {Dialog};
|
||||
class Options implements OptionsElement<void> {
|
||||
name: string;
|
||||
|
@ -744,6 +807,19 @@ class Options implements OptionsElement<void> {
|
|||
this.genTop();
|
||||
return options;
|
||||
}
|
||||
addEmojiInput(
|
||||
label: string,
|
||||
onSubmit: (str: Emoji | undefined) => void,
|
||||
localuser: Localuser,
|
||||
{initEmoji = undefined} = {} as {initEmoji?: Emoji},
|
||||
) {
|
||||
const emoji = new EmojiInput(label, onSubmit, this, localuser, {
|
||||
initEmoji: initEmoji,
|
||||
});
|
||||
this.options.push(emoji);
|
||||
this.generate(emoji);
|
||||
return emoji;
|
||||
}
|
||||
returnFromSub() {
|
||||
this.subOptions = undefined;
|
||||
this.genTop();
|
||||
|
@ -1127,6 +1203,21 @@ class Form implements OptionsElement<object> {
|
|||
}
|
||||
return FI;
|
||||
}
|
||||
addEmojiInput(
|
||||
label: string,
|
||||
formName: string,
|
||||
localuser: Localuser,
|
||||
{initEmoji = undefined, required = false} = {} as {initEmoji?: Emoji; required: boolean},
|
||||
) {
|
||||
const emoji = this.options.addEmojiInput(label, () => {}, localuser, {
|
||||
initEmoji: initEmoji,
|
||||
});
|
||||
if (required) {
|
||||
this.required.add(emoji);
|
||||
}
|
||||
this.names.set(formName, emoji);
|
||||
return emoji;
|
||||
}
|
||||
|
||||
addTextInput(
|
||||
label: string,
|
||||
|
@ -1294,6 +1385,15 @@ class Form implements OptionsElement<object> {
|
|||
} else {
|
||||
console.error(options.files + " is not currently implemented");
|
||||
}
|
||||
} else if (input instanceof EmojiInput) {
|
||||
if (!input.value) {
|
||||
(build as any)[thing] = undefined;
|
||||
} else if (input.value.id) {
|
||||
(build as any)[thing] = input.value.id;
|
||||
} else if (input.value.emoji) {
|
||||
(build as any)[thing] = input.value.emoji;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
(build as any)[thing] = input.value;
|
||||
}
|
||||
|
|
36
src/webpage/sticker.ts
Normal file
36
src/webpage/sticker.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
import {Guild} from "./guild.js";
|
||||
import {stickerJson} from "./jsontypes.js";
|
||||
import {SnowFlake} from "./snowflake.js";
|
||||
import {createImg} from "./utils/utils.js";
|
||||
|
||||
class Sticker extends SnowFlake {
|
||||
name: string;
|
||||
type: number;
|
||||
format_type: number;
|
||||
owner: Guild;
|
||||
description: string;
|
||||
tags: string;
|
||||
get guild() {
|
||||
return this.owner;
|
||||
}
|
||||
get localuser() {
|
||||
return this.owner.localuser;
|
||||
}
|
||||
constructor(json: stickerJson, owner: Guild) {
|
||||
super(json.id);
|
||||
this.name = json.name;
|
||||
this.type = json.type;
|
||||
this.format_type = json.format_type;
|
||||
this.owner = owner;
|
||||
this.tags = json.tags;
|
||||
this.description = json.description || "";
|
||||
}
|
||||
getHTML(): HTMLElement {
|
||||
const img = createImg(
|
||||
this.owner.info.cdn + "/stickers/" + this.id + ".webp?size=160&quality=lossless",
|
||||
);
|
||||
img.classList.add("sticker");
|
||||
return img;
|
||||
}
|
||||
}
|
||||
export {Sticker};
|
|
@ -2046,6 +2046,7 @@ img.bigembedimg {
|
|||
box-sizing: border-box;
|
||||
user-select: none;
|
||||
background: var(--secondary-bg);
|
||||
z-index: 4;
|
||||
|
||||
input {
|
||||
width: 1in;
|
||||
|
@ -2364,12 +2365,25 @@ fieldset input[type="radio"] {
|
|||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
.stickersDiv {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.webhookpfppreview {
|
||||
width: 0.8in;
|
||||
height: 0.8in;
|
||||
border-radius: 1in;
|
||||
margin-right: 0.2in;
|
||||
}
|
||||
.stickerView {
|
||||
max-width: 2.5in;
|
||||
max-height: 2.5in;
|
||||
}
|
||||
.sticker {
|
||||
max-width: 2.5in;
|
||||
max-height: 2.5in;
|
||||
}
|
||||
.optionElement,
|
||||
.FormSettings > button {
|
||||
margin: 16px 16px 0 16px;
|
||||
|
@ -2749,6 +2763,21 @@ fieldset input[type="radio"] {
|
|||
.friendlyButton:hover {
|
||||
background: black;
|
||||
}
|
||||
.stickerOption {
|
||||
border: solid 1px var(--black);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.075in;
|
||||
margin-bottom: 0.2in;
|
||||
border-radius: 0.1in;
|
||||
background: var(--primary-hover);
|
||||
position: relative;
|
||||
margin-right: 15px;
|
||||
cursor: pointer;
|
||||
img {
|
||||
height: 2in;
|
||||
}
|
||||
}
|
||||
.emojiOption {
|
||||
border: solid 1px var(--black);
|
||||
display: flex;
|
||||
|
@ -2833,8 +2862,29 @@ fieldset input[type="radio"] {
|
|||
max-width: 196px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
}
|
||||
.emojiForm {
|
||||
display: flex;
|
||||
background: var(--secondary-bg);
|
||||
padding: 6px;
|
||||
width: fit-content;
|
||||
border-radius: 4px;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
:last-child {
|
||||
margin-left: 6px;
|
||||
max-width: 32px !important;
|
||||
max-height: 32px !important;
|
||||
flex-shrink: 0;
|
||||
font-size: 28px;
|
||||
}
|
||||
}
|
||||
.emptyEmoji {
|
||||
background: var(--primary-bg);
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 2in;
|
||||
}
|
||||
|
|
|
@ -553,6 +553,18 @@
|
|||
"name:": "Name:",
|
||||
"confirmDel": "Are you sure you want to delete this emoji?"
|
||||
},
|
||||
"sticker": {
|
||||
"title": "Stickers",
|
||||
"upload": "Upload Stickers",
|
||||
"image": "Image:",
|
||||
"name": "Name:",
|
||||
"desc": "Description",
|
||||
"confirmDel": "Are you sure you want to delete this sticker?",
|
||||
"del": "Delete sticker",
|
||||
"errFileMust": "Must include an image for your sticker",
|
||||
"errEmjMust": "Must include an emoji with your sticker",
|
||||
"tags": "Associated Emoji: "
|
||||
},
|
||||
"widget": "Guild Widget",
|
||||
"widgetEnabled": "Widget enabled",
|
||||
"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?",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue