mirror of
https://github.com/MathMan05/Fermi.git
synced 2026-08-28 09:24:05 +00:00
make typebox into a seperate class for fewer bugs
This commit is contained in:
+23
-105
@@ -31,12 +31,12 @@ import {mobile, createImg, safeImg} from "./utils/utils.js";
|
||||
import {webhookMenu} from "./webhooks.js";
|
||||
import {File} from "./file.js";
|
||||
import {Sticker} from "./sticker.js";
|
||||
import {CustomHTMLDivElement} from "./index.js";
|
||||
import {Direct} from "./direct.js";
|
||||
import {NotificationHandler} from "./notificationHandler.js";
|
||||
import {Command} from "./interactions/commands.js";
|
||||
import {Tag} from "./tag.js";
|
||||
import {CDNParams} from "./utils/cdnParams.js";
|
||||
import {TypeBox} from "./typeBox.js";
|
||||
|
||||
class Channel extends SnowFlake {
|
||||
editing!: Message | null;
|
||||
@@ -1437,40 +1437,13 @@ class Channel extends SnowFlake {
|
||||
this.replyingto.div.classList.remove("replying");
|
||||
}
|
||||
this.replyingto = message;
|
||||
const typebox = document.getElementById("typebox") as HTMLElement;
|
||||
typebox.focus();
|
||||
TypeBox.focus();
|
||||
if (!this.replyingto?.div) return;
|
||||
console.log(message);
|
||||
this.replyingto.div.classList.add("replying");
|
||||
this.makereplybox();
|
||||
}
|
||||
makereplybox() {
|
||||
const replybox = document.getElementById("replybox") as HTMLElement;
|
||||
const typebox = document.getElementById("typebox") as HTMLElement;
|
||||
if (this.replyingto) {
|
||||
replybox.innerHTML = "";
|
||||
const span = document.createElement("span");
|
||||
span.textContent = I18n.replyingTo(this.replyingto.author.username);
|
||||
const X = document.createElement("button");
|
||||
X.onclick = (_) => {
|
||||
if (this.replyingto?.div) {
|
||||
this.replyingto.div.classList.remove("replying");
|
||||
}
|
||||
replybox.classList.add("hideReplyBox");
|
||||
this.replyingto = null;
|
||||
replybox.innerHTML = "";
|
||||
typebox.classList.remove("typeboxreplying");
|
||||
};
|
||||
replybox.classList.remove("hideReplyBox");
|
||||
X.classList.add("cancelReply", "svgicon", "svg-x");
|
||||
replybox.append(span);
|
||||
replybox.append(X);
|
||||
typebox.classList.add("typeboxreplying");
|
||||
} else {
|
||||
replybox.classList.add("hideReplyBox");
|
||||
replybox.innerHTML = "";
|
||||
typebox.classList.remove("typeboxreplying");
|
||||
}
|
||||
TypeBox.updateReplying();
|
||||
}
|
||||
async getmessage(id: string): Promise<Message | undefined> {
|
||||
const message = this.messages.get(id);
|
||||
@@ -1543,9 +1516,6 @@ class Channel extends SnowFlake {
|
||||
}
|
||||
static genid: number = 0;
|
||||
nsfwPannel() {
|
||||
(document.getElementById("typebox") as HTMLDivElement).contentEditable = "" + false;
|
||||
(document.getElementById("upload") as HTMLElement).style.visibility = "hidden";
|
||||
(document.getElementById("typediv") as HTMLElement).style.visibility = "hidden";
|
||||
const messages = document.getElementById("scrollWrap") as HTMLDivElement;
|
||||
const messageContainers = Array.from(messages.getElementsByClassName("messagecontainer"));
|
||||
for (const thing of messageContainers) {
|
||||
@@ -1953,49 +1923,35 @@ class Channel extends SnowFlake {
|
||||
}
|
||||
};
|
||||
}
|
||||
files: Blob[] = [];
|
||||
htmls = new WeakMap<Blob, HTMLElement>();
|
||||
textSave = "";
|
||||
collectBox() {
|
||||
const typebox = document.getElementById("typebox") as CustomHTMLDivElement;
|
||||
const [files, html] = this.localuser.fileExtange([], new WeakMap<Blob, HTMLElement>());
|
||||
this.files = files;
|
||||
this.htmls = html;
|
||||
this.textSave = MarkDown.gatherBoxText(typebox);
|
||||
typebox.textContent = "";
|
||||
}
|
||||
curCommand?: Command;
|
||||
curWatch = () => {};
|
||||
async submitCommand() {
|
||||
if (!this.curCommand) return;
|
||||
const typebox = document.getElementById("typebox") as CustomHTMLDivElement;
|
||||
if (await this.curCommand.submit(typebox, this)) {
|
||||
if (await this.curCommand.submit(TypeBox.box, this)) {
|
||||
this.curCommand = undefined;
|
||||
const typebox = document.getElementById("typebox") as CustomHTMLDivElement;
|
||||
typebox.markdown.boxEnabled = true;
|
||||
typebox.innerHTML = "";
|
||||
typebox.markdown.boxupdate();
|
||||
typebox.removeEventListener("keyup", this.curWatch);
|
||||
TypeBox.markdown.boxEnabled = true;
|
||||
TypeBox.box.innerHTML = "";
|
||||
TypeBox.markdown.boxupdate();
|
||||
TypeBox.box.removeEventListener("keyup", this.curWatch);
|
||||
}
|
||||
}
|
||||
startCommand(command: Command) {
|
||||
this.curCommand = command;
|
||||
const typebox = document.getElementById("typebox") as CustomHTMLDivElement;
|
||||
typebox.markdown.boxEnabled = false;
|
||||
TypeBox.markdown.boxEnabled = false;
|
||||
const func = () => {
|
||||
const node = window.getSelection()?.focusNode;
|
||||
if (this.localuser.focusChannel === this) {
|
||||
const out = command.collect(typebox, this, node || undefined);
|
||||
const out = command.collect(TypeBox.box, this, node || undefined);
|
||||
if (!out) {
|
||||
typebox.markdown.boxEnabled = true;
|
||||
typebox.markdown.boxupdate();
|
||||
typebox.removeEventListener("keyup", func);
|
||||
TypeBox.markdown.boxEnabled = true;
|
||||
TypeBox.markdown.boxupdate();
|
||||
TypeBox.box.removeEventListener("keyup", func);
|
||||
}
|
||||
}
|
||||
};
|
||||
this.curWatch = func;
|
||||
typebox.addEventListener("keyup", func);
|
||||
command.render(typebox, this);
|
||||
TypeBox.box.addEventListener("keyup", func);
|
||||
command.render(TypeBox.box, this);
|
||||
}
|
||||
isForum() {
|
||||
return this.type === 15 || this.type === 16;
|
||||
@@ -2624,28 +2580,18 @@ class Channel extends SnowFlake {
|
||||
if (this.owner instanceof Direct) {
|
||||
this.owner.freindDiv?.classList.remove("viewChannel");
|
||||
}
|
||||
if (this.localuser.focusChannel) {
|
||||
this.localuser.focusChannel.collectBox();
|
||||
TypeBox.saveBox();
|
||||
if (!this.curCommand && !this.isForum()) {
|
||||
TypeBox.restoreBox(this);
|
||||
}
|
||||
const typebox = document.getElementById("typebox") as CustomHTMLDivElement;
|
||||
typebox.markdown.boxEnabled = !this.curCommand;
|
||||
TypeBox.markdown.boxEnabled = !this.curCommand;
|
||||
if (this.curCommand) {
|
||||
this.curCommand.render(typebox, this);
|
||||
this.curCommand.render(TypeBox.box, this);
|
||||
}
|
||||
typebox.style.setProperty(
|
||||
TypeBox.box.style.setProperty(
|
||||
"--channel-text",
|
||||
JSON.stringify(I18n.channel.typebox(this.shortName)),
|
||||
);
|
||||
if (!this.curCommand && !this.isForum()) {
|
||||
const md = typebox.markdown;
|
||||
md.owner = this;
|
||||
typebox.textContent = this.textSave;
|
||||
md.boxupdate(Infinity);
|
||||
}
|
||||
if (this.isForum()) {
|
||||
typebox.textContent = "";
|
||||
}
|
||||
this.localuser.fileExtange(this.files, this.htmls);
|
||||
|
||||
if (getMessages === undefined) {
|
||||
getMessages = this.type !== 2 || !this.localuser.voiceAllowed;
|
||||
@@ -2761,36 +2707,8 @@ class Channel extends SnowFlake {
|
||||
}
|
||||
this.rendertyping();
|
||||
|
||||
try {
|
||||
(document.getElementById("typebox") as HTMLDivElement).contentEditable = this.canMessage
|
||||
? "plaintext-only"
|
||||
: "false";
|
||||
} catch {
|
||||
(document.getElementById("typebox") as HTMLDivElement).contentEditable = this.canMessage
|
||||
? "true"
|
||||
: "false";
|
||||
}
|
||||
(document.getElementById("upload") as HTMLElement).style.visibility = this.canMessage
|
||||
? "visible"
|
||||
: "hidden";
|
||||
(document.getElementById("gifTB") as HTMLElement).style.display = this.canMessage
|
||||
? "block"
|
||||
: "none";
|
||||
(document.getElementById("stickerTB") as HTMLElement).style.display = this.canMessage
|
||||
? "block"
|
||||
: "none";
|
||||
(document.getElementById("emojiTB") as HTMLElement).style.display = this.canMessage
|
||||
? "block"
|
||||
: "none";
|
||||
(document.getElementById("mobileSend") as HTMLElement).style.display = this.canMessage
|
||||
? "block"
|
||||
: "none";
|
||||
(document.getElementById("typediv") as HTMLElement).style.visibility = "visible";
|
||||
if (!mobile) {
|
||||
(document.getElementById("typebox") as HTMLDivElement).focus();
|
||||
} else {
|
||||
(document.getElementById("typebox") as HTMLDivElement).blur();
|
||||
}
|
||||
TypeBox.changeWrite();
|
||||
|
||||
try {
|
||||
if (getMessages) await this.putmessages();
|
||||
} catch (e) {
|
||||
|
||||
@@ -35,6 +35,7 @@ import {Hover} from "./hover.js";
|
||||
import {ReportMenu} from "./reporting/report.js";
|
||||
import {getDeveloperSettings} from "./utils/storage/devSettings.js";
|
||||
import {CDNParams} from "./utils/cdnParams.js";
|
||||
import {TypeBox} from "./typeBox.js";
|
||||
export async function makeInviteMenu(inviteMenu: Options, guild: Guild, url: string) {
|
||||
const invDiv = document.createElement("div");
|
||||
const bansp = ProgessiveDecodeJSON<invitejson[]>(url, {
|
||||
@@ -1955,16 +1956,9 @@ class Guild extends SnowFlake {
|
||||
if (this.localuser.focusChannel && this.localuser.focusChannel.myhtml) {
|
||||
this.localuser.focusChannel.myhtml.classList.remove("viewChannel");
|
||||
}
|
||||
TypeBox.changeVisablity(false);
|
||||
this.prevchannel = undefined;
|
||||
this.localuser.focusChannel = undefined;
|
||||
const replybox = document.getElementById("replybox") as HTMLElement;
|
||||
const typebox = document.getElementById("typebox") as HTMLElement;
|
||||
replybox.classList.add("hideReplyBox");
|
||||
typebox.classList.remove("typeboxreplying");
|
||||
(document.getElementById("typebox") as HTMLDivElement).contentEditable = "false";
|
||||
(document.getElementById("upload") as HTMLElement).style.visibility = "hidden";
|
||||
(document.getElementById("typediv") as HTMLElement).style.visibility = "hidden";
|
||||
(document.getElementById("sideDiv") as HTMLElement).innerHTML = "";
|
||||
}
|
||||
noChannel(addstate: boolean) {
|
||||
for (const c of this.channels) {
|
||||
|
||||
+5
-261
@@ -3,8 +3,6 @@ import {Contextmenu} from "./contextmenu.js";
|
||||
import {mobile, Specialuser} from "./utils/utils.js";
|
||||
import {setTheme} from "./utils/utils.js";
|
||||
import {MarkDown} from "./markdown.js";
|
||||
import {Message} from "./message.js";
|
||||
import {File} from "./file.js";
|
||||
import {I18n} from "./i18n.js";
|
||||
import "./utils/pollyfills.js";
|
||||
import {makeLogin} from "./login.js";
|
||||
@@ -17,8 +15,8 @@ import "./invite.js";
|
||||
import "./oauth2/auth.js";
|
||||
import "./audio/page.js";
|
||||
import "./404.js";
|
||||
import {Channel} from "./channel.js";
|
||||
import type * as C from "./typeChecker/chekerIndex.js";
|
||||
import {TypeBox} from "./typeBox.js";
|
||||
|
||||
if (window.location.pathname === "/app") {
|
||||
window.location.pathname = "/channels/@me";
|
||||
@@ -72,35 +70,7 @@ if (window.location.pathname.startsWith("/channels")) {
|
||||
});
|
||||
|
||||
let thisUser: Localuser | null = null;
|
||||
function regSwap(l: Localuser) {
|
||||
l.onswap = (l) => {
|
||||
thisUser = l;
|
||||
regSwap(l);
|
||||
};
|
||||
l.fileExtange = (img, html) => {
|
||||
const blobArr: Blob[] = [];
|
||||
const htmlArr = imagesHtml;
|
||||
let i = 0;
|
||||
for (const file of images) {
|
||||
const img = imagesHtml.get(file);
|
||||
if (!img) continue;
|
||||
if (pasteImageElement.contains(img)) {
|
||||
pasteImageElement.removeChild(img);
|
||||
blobArr.push(images[i]);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
images = img;
|
||||
imagesHtml = html;
|
||||
for (const file of images) {
|
||||
const img = imagesHtml.get(file);
|
||||
if (!img) throw new Error("Image without HTML, exiting");
|
||||
pasteImageElement.append(img);
|
||||
}
|
||||
return [blobArr, htmlArr];
|
||||
};
|
||||
}
|
||||
|
||||
const loaddesc = document.getElementById("load-desc") as HTMLSpanElement;
|
||||
try {
|
||||
const current = sessionStorage.getItem("currentuser") || Localuser.users.currentuser;
|
||||
@@ -110,8 +80,8 @@ if (window.location.pathname.startsWith("/channels")) {
|
||||
thisUser = new Localuser(Localuser.users.users[current]);
|
||||
}
|
||||
|
||||
regSwap(thisUser);
|
||||
thisUser.initwebsocket().then(async () => {
|
||||
if (thisUser) TypeBox.regSwap(thisUser);
|
||||
thisUser?.loaduser();
|
||||
console.warn("huh");
|
||||
await thisUser?.init();
|
||||
@@ -160,151 +130,16 @@ if (window.location.pathname.startsWith("/channels")) {
|
||||
},
|
||||
},
|
||||
);
|
||||
const channelw = document.getElementById("channelw");
|
||||
if (channelw)
|
||||
channelw.addEventListener("keypress", (e) => {
|
||||
if (e.ctrlKey || e.altKey || e.metaKey || e.metaKey) return;
|
||||
let owner = e.target as HTMLElement;
|
||||
while (owner !== channelw) {
|
||||
if (owner.tagName === "input" || owner.contentEditable !== "false") {
|
||||
return;
|
||||
}
|
||||
owner = owner.parentElement as HTMLElement;
|
||||
}
|
||||
typebox.markdown.boxupdate(Infinity);
|
||||
});
|
||||
|
||||
menu.bindContextmenu(document.getElementById("channels") as HTMLDivElement);
|
||||
|
||||
const pasteImageElement = document.getElementById("pasteimage") as HTMLDivElement;
|
||||
let replyingTo: Message | null = null;
|
||||
window.addEventListener("popstate", (e) => {
|
||||
if (e.state instanceof Object) {
|
||||
thisUser?.goToState(e.state);
|
||||
}
|
||||
//console.log(e.state,"state:3")
|
||||
});
|
||||
let nonceMap = new Map<string, string>();
|
||||
//@ts-expect-error unused right now, not needed
|
||||
function getNonce(id: string) {
|
||||
const nonce = nonceMap.get(id) || Math.floor(Math.random() * 1000000000) + "";
|
||||
nonceMap.set(id, nonce);
|
||||
return nonce;
|
||||
}
|
||||
const markdown = new MarkDown("", thisUser ?? undefined);
|
||||
async function sendMessage(channel: Channel, content: string) {
|
||||
if (!channel.canMessageRightNow()) return;
|
||||
if (channel.curCommand) {
|
||||
channel.submitCommand();
|
||||
return;
|
||||
}
|
||||
markdown.onUpdate("", false);
|
||||
|
||||
replyingTo = thisUser?.focusChannel ? thisUser.focusChannel.replyingto : null;
|
||||
if (replyingTo?.div) {
|
||||
replyingTo.div.classList.remove("replying");
|
||||
}
|
||||
if (thisUser?.focusChannel) {
|
||||
thisUser.focusChannel.replyingto = null;
|
||||
thisUser.focusChannel.makereplybox();
|
||||
}
|
||||
const attachments = images.filter((_) => document.contains(imagesHtml.get(_) || null));
|
||||
while (images.length) {
|
||||
const elm = imagesHtml.get(images.pop() as Blob) as HTMLElement;
|
||||
if (pasteImageElement.contains(elm)) pasteImageElement.removeChild(elm);
|
||||
}
|
||||
typebox.innerHTML = "";
|
||||
typebox.markdown.txt = [];
|
||||
try {
|
||||
await new Promise<void>((mres, rej) =>
|
||||
channel.sendMessage(
|
||||
content,
|
||||
{
|
||||
attachments,
|
||||
embeds: [], // Add an empty array for the embeds property
|
||||
replyingto: replyingTo,
|
||||
sticker_ids: [],
|
||||
//nonce: getNonce(channel.id),
|
||||
},
|
||||
(res) => {
|
||||
if (res === "Ok") {
|
||||
mres();
|
||||
} else {
|
||||
rej();
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
} catch {
|
||||
images = attachments;
|
||||
for (const file of images) {
|
||||
const img = imagesHtml.get(file);
|
||||
if (!img) continue;
|
||||
pasteImageElement.append(img);
|
||||
}
|
||||
channel.replyingto = replyingTo;
|
||||
channel.makereplybox();
|
||||
typebox.textContent = content;
|
||||
typebox.markdown.txt = content.split("");
|
||||
typebox.markdown.boxupdate(Infinity);
|
||||
}
|
||||
nonceMap.delete(channel.id);
|
||||
}
|
||||
const mobileSend = document.getElementById("mobileSend");
|
||||
if (mobileSend) {
|
||||
mobileSend.onclick = () => {
|
||||
const channel = thisUser?.focusChannel;
|
||||
if (!channel) return;
|
||||
const content = MarkDown.gatherBoxText(typebox);
|
||||
sendMessage(channel, content);
|
||||
};
|
||||
}
|
||||
async function handleEnter(event: KeyboardEvent): Promise<void> {
|
||||
if (event.isComposing) return;
|
||||
if (event.key === "Escape" && (images.length || thisUser?.focusChannel?.replyingto)) {
|
||||
while (images.length) {
|
||||
const elm = imagesHtml.get(images.pop() as Blob) as HTMLElement;
|
||||
if (pasteImageElement.contains(elm)) pasteImageElement.removeChild(elm);
|
||||
}
|
||||
if (thisUser?.focusChannel) {
|
||||
thisUser.focusChannel?.replyingto?.div?.classList.remove("replying");
|
||||
thisUser.focusChannel.replyingto = null;
|
||||
thisUser.focusChannel.makereplybox();
|
||||
}
|
||||
thisUser?.updateSend();
|
||||
return;
|
||||
}
|
||||
if (thisUser?.handleKeyUp(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const channel = thisUser?.focusChannel;
|
||||
if (!channel) return;
|
||||
const content = MarkDown.gatherBoxText(typebox);
|
||||
if (content === "" && event.key === "ArrowUp") {
|
||||
channel.editLast();
|
||||
return;
|
||||
}
|
||||
channel.typingstart();
|
||||
|
||||
if (event.key === "Enter" && !event.shiftKey && window.innerWidth > 600) {
|
||||
event.preventDefault();
|
||||
await sendMessage(channel, content);
|
||||
}
|
||||
}
|
||||
|
||||
const typebox = document.getElementById("typebox") as CustomHTMLDivElement;
|
||||
|
||||
typebox.markdown = markdown;
|
||||
typebox.addEventListener("keyup", handleEnter);
|
||||
typebox.addEventListener("keydown", (event) => {
|
||||
if (event.isComposing) return;
|
||||
thisUser?.keydown(event);
|
||||
if (event.key === "Enter" && !event.shiftKey && window.innerWidth > 600) {
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
}
|
||||
});
|
||||
markdown.giveBox(typebox);
|
||||
{
|
||||
const searchBox = document.getElementById("searchBox") as CustomHTMLDivElement;
|
||||
const markdown = new MarkDown("", thisUser ?? undefined);
|
||||
@@ -352,25 +187,6 @@ if (window.location.pathname.startsWith("/channels")) {
|
||||
return span;
|
||||
});
|
||||
}
|
||||
let images: Blob[] = [];
|
||||
let imagesHtml = new WeakMap<Blob, HTMLElement>();
|
||||
|
||||
document.addEventListener("paste", async (e: ClipboardEvent) => {
|
||||
if (!thisUser?.focusChannel) return;
|
||||
if (!e.clipboardData) return;
|
||||
|
||||
for (const file of Array.from(e.clipboardData.files)) {
|
||||
const fileInstance = File.initFromBlob(file);
|
||||
e.preventDefault();
|
||||
const html = fileInstance.upHTML(images, imagesHtml, file, () => {
|
||||
thisUser?.updateSend();
|
||||
});
|
||||
pasteImageElement.appendChild(html);
|
||||
images.push(file);
|
||||
imagesHtml.set(file, html);
|
||||
}
|
||||
thisUser?.updateSend();
|
||||
});
|
||||
|
||||
await setTheme();
|
||||
|
||||
@@ -396,60 +212,7 @@ if (window.location.pathname.startsWith("/channels")) {
|
||||
};
|
||||
memberListToggle.checked = false;
|
||||
}
|
||||
let dragendtimeout = setTimeout(() => {});
|
||||
document.addEventListener("dragover", (e) => {
|
||||
clearTimeout(dragendtimeout);
|
||||
const data = e.dataTransfer;
|
||||
const bg = document.getElementById("gimmefile") as HTMLDivElement;
|
||||
|
||||
if (data) {
|
||||
const isfile = data.types.includes("Files") || data.types.includes("application/x-moz-file");
|
||||
if (!isfile) {
|
||||
bg.hidden = true;
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
bg.hidden = false;
|
||||
//console.log(data.types,data)
|
||||
} else {
|
||||
bg.hidden = true;
|
||||
}
|
||||
});
|
||||
document.addEventListener("dragleave", (_) => {
|
||||
dragendtimeout = setTimeout(() => {
|
||||
const bg = document.getElementById("gimmefile") as HTMLDivElement;
|
||||
bg.hidden = true;
|
||||
}, 1000);
|
||||
});
|
||||
document.addEventListener("dragenter", (e) => {
|
||||
e.preventDefault();
|
||||
});
|
||||
document.addEventListener("drop", (e) => {
|
||||
const data = e.dataTransfer;
|
||||
const bg = document.getElementById("gimmefile") as HTMLDivElement;
|
||||
bg.hidden = true;
|
||||
if (!thisUser?.focusChannel) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
if (data) {
|
||||
const isfile = data.types.includes("Files") || data.types.includes("application/x-moz-file");
|
||||
if (isfile) {
|
||||
e.preventDefault();
|
||||
console.log(data.files);
|
||||
for (const file of Array.from(data.files)) {
|
||||
const fileInstance = File.initFromBlob(file);
|
||||
const html = fileInstance.upHTML(images, imagesHtml, file, () => {
|
||||
thisUser?.updateSend();
|
||||
});
|
||||
pasteImageElement.appendChild(html);
|
||||
images.push(file);
|
||||
imagesHtml.set(file, html);
|
||||
}
|
||||
thisUser?.updateSend();
|
||||
}
|
||||
}
|
||||
});
|
||||
const pinnedM = document.getElementById("pinnedM") as HTMLElement;
|
||||
pinnedM.onclick = (e) => {
|
||||
thisUser?.pinnedClick(pinnedM.getBoundingClientRect());
|
||||
@@ -468,26 +231,7 @@ if (window.location.pathname.startsWith("/channels")) {
|
||||
},
|
||||
);
|
||||
umenu.addButton(I18n.upload(), () => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.click();
|
||||
input.multiple = true;
|
||||
console.log("clicked");
|
||||
if (!thisUser?.focusChannel) return;
|
||||
input.onchange = () => {
|
||||
if (input.files) {
|
||||
for (const file of Array.from(input.files)) {
|
||||
const fileInstance = File.initFromBlob(file);
|
||||
const html = fileInstance.upHTML(images, imagesHtml, file, () => {
|
||||
thisUser?.updateSend();
|
||||
});
|
||||
pasteImageElement.appendChild(html);
|
||||
images.push(file);
|
||||
imagesHtml.set(file, html);
|
||||
}
|
||||
thisUser?.updateSend();
|
||||
}
|
||||
};
|
||||
TypeBox.uploadFiles();
|
||||
});
|
||||
umenu.bindContextmenu(
|
||||
document.getElementById("upload")!,
|
||||
|
||||
@@ -53,15 +53,14 @@ import {trimTrailingSlashes} from "./utils/netUtils.js";
|
||||
import {Versions} from "./versions.js";
|
||||
import {Shortcut} from "./shortcuts/shortcut.js";
|
||||
import {getShortcuts, setShortcuts} from "./utils/storage/shortcuts.js";
|
||||
import {TypeBox} from "./typeBox.js";
|
||||
type traceObj = {
|
||||
micros: number;
|
||||
calls?: (string | traceObj)[];
|
||||
};
|
||||
type trace = [string, traceObj];
|
||||
const wsCodesRetry = new Set([4000, 4001, 4002, 4003, 4005, 4007, 4008, 4009]);
|
||||
interface CustomHTMLDivElement extends HTMLDivElement {
|
||||
markdown: MarkDown;
|
||||
}
|
||||
|
||||
interface MDSearchOption {
|
||||
name: string;
|
||||
replace: string;
|
||||
@@ -1755,6 +1754,7 @@ class Localuser {
|
||||
}
|
||||
|
||||
loadGuild(id: string, forceReload = false): Guild | undefined {
|
||||
TypeBox.saveBox();
|
||||
this.searching = false;
|
||||
let guild = this.guilds.get(id);
|
||||
if (!guild) {
|
||||
@@ -4280,21 +4280,12 @@ class Localuser {
|
||||
});
|
||||
}
|
||||
updateSend() {
|
||||
const typebox = document.getElementById("typebox") as CustomHTMLDivElement;
|
||||
if (
|
||||
(typebox.markdown.rawString && typebox.markdown.rawString !== "\n") ||
|
||||
document.getElementById("pasteimage")?.children.length
|
||||
) {
|
||||
typebox.parentElement!.classList.remove("noConent");
|
||||
} else {
|
||||
typebox.parentElement!.classList.add("noConent");
|
||||
}
|
||||
TypeBox.updateSend();
|
||||
}
|
||||
//TODO make this an option
|
||||
readonly autofillregex = Object.freeze(/(^|\s|\n)[@#:]([a-zA-Z0-9]*)$/i);
|
||||
mdBox() {
|
||||
const typebox = document.getElementById("typebox") as CustomHTMLDivElement;
|
||||
const typeMd = typebox.markdown;
|
||||
const typeMd = TypeBox.markdown;
|
||||
typeMd.owner = this;
|
||||
typeMd.onUpdate = (str, pre) => {
|
||||
this.search(document.getElementById("searchOptions") as HTMLDivElement, typeMd, str, pre);
|
||||
@@ -4529,8 +4520,7 @@ class Localuser {
|
||||
search.focus();
|
||||
}
|
||||
async TBEmojiMenu(rect: DOMRect) {
|
||||
const typebox = document.getElementById("typebox") as CustomHTMLDivElement;
|
||||
const p = saveCaretPosition(typebox);
|
||||
const p = TypeBox.saveCarrot();
|
||||
if (!p) return;
|
||||
const original = MarkDown.getText();
|
||||
|
||||
@@ -4541,7 +4531,7 @@ class Localuser {
|
||||
);
|
||||
this.favorites.addEmoji(emoji.id || (emoji.emoji as string));
|
||||
p();
|
||||
const md = typebox.markdown;
|
||||
const md = TypeBox.markdown;
|
||||
this.MDReplace(
|
||||
emoji.id
|
||||
? `<${emoji.animated ? "a" : ""}:${emoji.name}:${emoji.id}>`
|
||||
@@ -4576,10 +4566,6 @@ class Localuser {
|
||||
);
|
||||
}
|
||||
}
|
||||
fileExtange!: (
|
||||
files: Blob[],
|
||||
html: WeakMap<Blob, HTMLElement>,
|
||||
) => [Blob[], WeakMap<Blob, HTMLElement>];
|
||||
MDSearchOptions(
|
||||
options: MDSearchOption[],
|
||||
original: string,
|
||||
|
||||
@@ -0,0 +1,363 @@
|
||||
import {Channel} from "./channel";
|
||||
import {Localuser} from "./localuser";
|
||||
import {MarkDown, saveCaretPosition} from "./markdown";
|
||||
import {File} from "./file";
|
||||
import {I18n} from "./i18n";
|
||||
import {mobile} from "./utils/utils";
|
||||
class BoxState {
|
||||
text: string;
|
||||
files: globalThis.File[];
|
||||
constructor(text: string, files: globalThis.File[]) {
|
||||
this.text = text;
|
||||
this.files = files;
|
||||
}
|
||||
}
|
||||
export class TypeBox {
|
||||
static box = document.getElementById("typebox") as HTMLDivElement;
|
||||
private static localuser?: Localuser;
|
||||
static markdown = new MarkDown("");
|
||||
private static files: globalThis.File[] = [];
|
||||
private static imagesHtml = new WeakMap<Blob, HTMLElement>();
|
||||
private static pasteImageElement = document.getElementById("pasteimage") as HTMLDivElement;
|
||||
private static nonceMap = new Map<string, string>();
|
||||
static getNonce(id: string) {
|
||||
const nonce = this.nonceMap.get(id) || Math.floor(Math.random() * 1000000000) + "";
|
||||
this.nonceMap.set(id, nonce);
|
||||
return nonce;
|
||||
}
|
||||
static focus() {
|
||||
this.box.focus();
|
||||
}
|
||||
static init() {
|
||||
this.box.addEventListener("keyup", this.handleEnter.bind(this));
|
||||
this.box.addEventListener("keydown", (event) => {
|
||||
if (event.isComposing) return;
|
||||
this.localuser?.keydown(event);
|
||||
if (event.key === "Enter" && !event.shiftKey && window.innerWidth > 600) {
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
}
|
||||
});
|
||||
this.markdown.giveBox(this.box);
|
||||
const mobileSend = document.getElementById("mobileSend");
|
||||
if (mobileSend) {
|
||||
mobileSend.onclick = () => {
|
||||
const channel = this.localuser?.focusChannel;
|
||||
if (!channel) return;
|
||||
const content = MarkDown.gatherBoxText(this.box);
|
||||
this.sendMessage(channel, content);
|
||||
};
|
||||
}
|
||||
const channelw = document.getElementById("channelw");
|
||||
if (channelw)
|
||||
channelw.addEventListener("keypress", (e) => {
|
||||
if (e.ctrlKey || e.altKey || e.metaKey || e.metaKey) return;
|
||||
let owner = e.target as HTMLElement;
|
||||
while (owner !== channelw) {
|
||||
if (owner.tagName === "input" || owner.contentEditable !== "false") {
|
||||
return;
|
||||
}
|
||||
owner = owner.parentElement as HTMLElement;
|
||||
}
|
||||
this.markdown.boxupdate(Infinity);
|
||||
});
|
||||
document.addEventListener("paste", async (e: ClipboardEvent) => {
|
||||
if (!this.localuser?.focusChannel) return;
|
||||
if (!e.clipboardData) return;
|
||||
|
||||
for (const file of Array.from(e.clipboardData.files)) {
|
||||
e.preventDefault();
|
||||
this.addFile(file);
|
||||
}
|
||||
this.localuser?.updateSend();
|
||||
});
|
||||
let dragendtimeout = setTimeout(() => {});
|
||||
document.addEventListener("dragover", (e) => {
|
||||
clearTimeout(dragendtimeout);
|
||||
const data = e.dataTransfer;
|
||||
const bg = document.getElementById("gimmefile") as HTMLDivElement;
|
||||
|
||||
if (data) {
|
||||
const isfile =
|
||||
data.types.includes("Files") || data.types.includes("application/x-moz-file");
|
||||
if (!isfile) {
|
||||
bg.hidden = true;
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
bg.hidden = false;
|
||||
//console.log(data.types,data)
|
||||
} else {
|
||||
bg.hidden = true;
|
||||
}
|
||||
});
|
||||
document.addEventListener("dragleave", (_) => {
|
||||
dragendtimeout = setTimeout(() => {
|
||||
const bg = document.getElementById("gimmefile") as HTMLDivElement;
|
||||
bg.hidden = true;
|
||||
}, 1000);
|
||||
});
|
||||
document.addEventListener("dragenter", (e) => {
|
||||
e.preventDefault();
|
||||
});
|
||||
document.addEventListener("drop", (e) => {
|
||||
const data = e.dataTransfer;
|
||||
const bg = document.getElementById("gimmefile") as HTMLDivElement;
|
||||
bg.hidden = true;
|
||||
if (!this.localuser?.focusChannel) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
if (data) {
|
||||
const isfile =
|
||||
data.types.includes("Files") || data.types.includes("application/x-moz-file");
|
||||
if (isfile) {
|
||||
e.preventDefault();
|
||||
console.log(data.files);
|
||||
for (const file of Array.from(data.files)) {
|
||||
this.addFile(file);
|
||||
}
|
||||
this.localuser?.updateSend();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
private static cMap = new Map<string, BoxState>();
|
||||
private static channelToID(c: Channel) {
|
||||
return c.id;
|
||||
}
|
||||
static saveBox() {
|
||||
if (!this.localuser?.focusChannel) return;
|
||||
const state = new BoxState(MarkDown.gatherBoxText(this.box), this.files);
|
||||
this.pasteImageElement.textContent = "";
|
||||
this.box.textContent = "";
|
||||
this.files = [];
|
||||
this.markdown.txt = [];
|
||||
this.cMap.set(this.channelToID(this.localuser.focusChannel), state);
|
||||
}
|
||||
static restoreBox(c = this.localuser?.focusChannel) {
|
||||
if (!c) return;
|
||||
const state = this.cMap.get(this.channelToID(c));
|
||||
this.box.textContent = "";
|
||||
this.pasteImageElement.textContent = "";
|
||||
if (state) {
|
||||
this.markdown.txt = [...state.text];
|
||||
this.files = state.files;
|
||||
} else {
|
||||
this.markdown.txt = [];
|
||||
this.files = [];
|
||||
}
|
||||
this.box.append(this.markdown.makeHTML({keep: true}));
|
||||
this.markdown.owner = c;
|
||||
this.markdown.boxupdate(Infinity);
|
||||
for (const file of this.files) {
|
||||
this.addFile(file, false);
|
||||
}
|
||||
}
|
||||
static changeWrite() {
|
||||
if (!this.localuser?.focusChannel) return;
|
||||
const c = this.localuser.focusChannel;
|
||||
const canMessage = c.canMessage;
|
||||
try {
|
||||
this.box.contentEditable = canMessage ? "plaintext-only" : "false";
|
||||
} catch {
|
||||
this.box.contentEditable = canMessage ? "true" : "false";
|
||||
}
|
||||
(document.getElementById("upload") as HTMLElement).style.visibility = canMessage
|
||||
? "visible"
|
||||
: "hidden";
|
||||
(document.getElementById("gifTB") as HTMLElement).style.display = canMessage ? "block" : "none";
|
||||
(document.getElementById("stickerTB") as HTMLElement).style.display = canMessage
|
||||
? "block"
|
||||
: "none";
|
||||
(document.getElementById("emojiTB") as HTMLElement).style.display = canMessage
|
||||
? "block"
|
||||
: "none";
|
||||
(document.getElementById("mobileSend") as HTMLElement).style.display = canMessage
|
||||
? "block"
|
||||
: "none";
|
||||
(document.getElementById("typediv") as HTMLElement).style.visibility = "visible";
|
||||
if (!mobile) {
|
||||
this.box.focus();
|
||||
} else {
|
||||
this.box.blur();
|
||||
}
|
||||
}
|
||||
static saveCarrot() {
|
||||
return saveCaretPosition(this.box);
|
||||
}
|
||||
static updateSend() {
|
||||
if (
|
||||
(this.markdown.rawString && this.markdown.rawString !== "\n") ||
|
||||
document.getElementById("pasteimage")?.children.length
|
||||
) {
|
||||
this.box.parentElement!.classList.remove("noConent");
|
||||
} else {
|
||||
this.box.parentElement!.classList.add("noConent");
|
||||
}
|
||||
}
|
||||
static changeVisablity(visable: boolean) {
|
||||
if (visable) {
|
||||
} else {
|
||||
this.box.contentEditable = "" + false;
|
||||
const replybox = document.getElementById("replybox") as HTMLElement;
|
||||
replybox.classList.add("hideReplyBox");
|
||||
this.box.classList.remove("typeboxreplying");
|
||||
(document.getElementById("upload") as HTMLElement).style.visibility = "hidden";
|
||||
(document.getElementById("typediv") as HTMLElement).style.visibility = "hidden";
|
||||
(document.getElementById("sideDiv") as HTMLElement).innerHTML = "";
|
||||
}
|
||||
}
|
||||
static addFile(blob: globalThis.File, add = true) {
|
||||
const file = File.initFromBlob(blob);
|
||||
const html = file.upHTML(this.files, this.imagesHtml, blob, () => {
|
||||
this.localuser?.updateSend();
|
||||
});
|
||||
this.pasteImageElement.appendChild(html);
|
||||
if (add) this.files.push(blob);
|
||||
this.imagesHtml.set(blob, html);
|
||||
}
|
||||
static updateReplying() {
|
||||
const c = this.localuser?.focusChannel;
|
||||
const replybox = document.getElementById("replybox") as HTMLElement;
|
||||
if (c && c.replyingto) {
|
||||
this.box.classList.add("typeboxreplying");
|
||||
replybox.innerHTML = "";
|
||||
const span = document.createElement("span");
|
||||
span.textContent = I18n.replyingTo(c.replyingto.author.username);
|
||||
const X = document.createElement("button");
|
||||
X.onclick = (_) => {
|
||||
if (c.replyingto?.div) {
|
||||
c.replyingto.div.classList.remove("replying");
|
||||
}
|
||||
replybox.classList.add("hideReplyBox");
|
||||
c.replyingto = null;
|
||||
replybox.innerHTML = "";
|
||||
TypeBox.updateReplying();
|
||||
};
|
||||
replybox.classList.remove("hideReplyBox");
|
||||
X.classList.add("cancelReply", "svgicon", "svg-x");
|
||||
replybox.append(span);
|
||||
replybox.append(X);
|
||||
} else {
|
||||
replybox.classList.add("hideReplyBox");
|
||||
replybox.innerHTML = "";
|
||||
this.box.classList.remove("typeboxreplying");
|
||||
}
|
||||
}
|
||||
static uploadFiles() {
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.click();
|
||||
input.multiple = true;
|
||||
console.log("clicked");
|
||||
if (!this.localuser?.focusChannel) return;
|
||||
input.onchange = () => {
|
||||
if (input.files) {
|
||||
for (const file of Array.from(input.files)) {
|
||||
this.addFile(file);
|
||||
}
|
||||
this.localuser?.updateSend();
|
||||
}
|
||||
};
|
||||
}
|
||||
static regSwap(l: Localuser) {
|
||||
l.onswap = (l) => {
|
||||
this.localuser = l;
|
||||
this.regSwap(l);
|
||||
};
|
||||
this.localuser = l;
|
||||
}
|
||||
private static async handleEnter(event: KeyboardEvent): Promise<void> {
|
||||
if (event.isComposing) return;
|
||||
if (event.key === "Escape" && (this.files.length || this.localuser?.focusChannel?.replyingto)) {
|
||||
while (this.files.length) {
|
||||
const elm = this.imagesHtml.get(this.files.pop() as Blob) as HTMLElement;
|
||||
if (this.pasteImageElement.contains(elm)) this.pasteImageElement.removeChild(elm);
|
||||
}
|
||||
if (this.localuser?.focusChannel) {
|
||||
this.localuser.focusChannel?.replyingto?.div?.classList.remove("replying");
|
||||
this.localuser.focusChannel.replyingto = null;
|
||||
this.localuser.focusChannel.makereplybox();
|
||||
}
|
||||
this.localuser?.updateSend();
|
||||
return;
|
||||
}
|
||||
if (this.localuser?.handleKeyUp(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const channel = this.localuser?.focusChannel;
|
||||
if (!channel) return;
|
||||
const content = MarkDown.gatherBoxText(this.box);
|
||||
if (content === "" && event.key === "ArrowUp") {
|
||||
channel.editLast();
|
||||
return;
|
||||
}
|
||||
channel.typingstart();
|
||||
|
||||
if (event.key === "Enter" && !event.shiftKey && window.innerWidth > 600) {
|
||||
event.preventDefault();
|
||||
await this.sendMessage(channel, content);
|
||||
}
|
||||
}
|
||||
private static async sendMessage(channel: Channel, content: string) {
|
||||
if (!channel.canMessageRightNow()) return;
|
||||
if (channel.curCommand) {
|
||||
channel.submitCommand();
|
||||
return;
|
||||
}
|
||||
this.markdown.onUpdate("", false);
|
||||
|
||||
let replyingTo = this.localuser?.focusChannel ? this.localuser.focusChannel.replyingto : null;
|
||||
if (replyingTo?.div) {
|
||||
replyingTo.div.classList.remove("replying");
|
||||
}
|
||||
if (this.localuser?.focusChannel) {
|
||||
this.localuser.focusChannel.replyingto = null;
|
||||
this.localuser.focusChannel.makereplybox();
|
||||
}
|
||||
const attachments = this.files.filter((_) => document.contains(this.imagesHtml.get(_) || null));
|
||||
while (this.files.length) {
|
||||
const elm = this.imagesHtml.get(this.files.pop() as Blob) as HTMLElement;
|
||||
if (this.pasteImageElement.contains(elm)) this.pasteImageElement.removeChild(elm);
|
||||
}
|
||||
this.box.innerHTML = "";
|
||||
this.markdown.txt = [];
|
||||
try {
|
||||
await new Promise<void>((mres, rej) =>
|
||||
channel.sendMessage(
|
||||
content,
|
||||
{
|
||||
attachments,
|
||||
embeds: [], // Add an empty array for the embeds property
|
||||
replyingto: replyingTo,
|
||||
sticker_ids: [],
|
||||
//nonce: getNonce(channel.id),
|
||||
},
|
||||
(res) => {
|
||||
if (res === "Ok") {
|
||||
mres();
|
||||
} else {
|
||||
rej();
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
} catch {
|
||||
this.files = attachments;
|
||||
for (const file of this.files) {
|
||||
const img = this.imagesHtml.get(file);
|
||||
if (!img) continue;
|
||||
this.pasteImageElement.append(img);
|
||||
}
|
||||
channel.replyingto = replyingTo;
|
||||
channel.makereplybox();
|
||||
this.box.textContent = content;
|
||||
this.markdown.txt = content.split("");
|
||||
this.markdown.boxupdate(Infinity);
|
||||
}
|
||||
this.nonceMap.delete(channel.id);
|
||||
}
|
||||
}
|
||||
TypeBox.init();
|
||||
Reference in New Issue
Block a user