diff --git a/package.json b/package.json index bf586ba..e82819f 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,12 @@ "prettier": "^3.4.2", "rimraf": "^6.0.1" }, + "prettier":{ + "useTabs":true, + "printWidth":100, + "semi":true, + "bracketSpacing":false + }, "devDependencies": { "@eslint/js": "^9.10.0", "@html-eslint/eslint-plugin": "^0.25.0", diff --git a/src/index.ts b/src/index.ts index 1b6040f..6b9094e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,176 +1,180 @@ #!/usr/bin/env node -import compression from"compression"; -import express, { Request, Response }from"express"; -import fs from"node:fs/promises"; -import path from"node:path"; -import{ observe, uptime }from"./stats.js"; -import{ getApiUrls, inviteResponse }from"./utils.js"; -import{ fileURLToPath }from"node:url"; +import compression from "compression"; +import express, {Request, Response} from "express"; +import fs from "node:fs/promises"; +import path from "node:path"; +import {observe, uptime} from "./stats.js"; +import {getApiUrls, inviteResponse} from "./utils.js"; +import {fileURLToPath} from "node:url"; import {readFileSync} from "fs"; -import process from"node:process"; +import process from "node:process"; const devmode = (process.env.NODE_ENV || "development") === "development"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); interface Instance { - name: string; - [key: string]: any; + name: string; + [key: string]: any; } const app = express(); -type instace={ - name:string, - description?:string, - descriptionLong?:string, - image?:string, - url?:string, - language:string, - country:string, - display:boolean, - urls?:{ - wellknown:string, - api:string, - cdn:string, - gateway:string, - login?:string - }, - contactInfo?:{ - discord?:string, - github?:string, - email?:string, - spacebar?:string, - matrix?:string, - mastodon?:string - } -} -const instances=JSON.parse(readFileSync(process.env.JANK_INSTANCES_PATH||(__dirname+"/webpage/instances.json")).toString()) as instace[]; +type instace = { + name: string; + description?: string; + descriptionLong?: string; + image?: string; + url?: string; + language: string; + country: string; + display: boolean; + urls?: { + wellknown: string; + api: string; + cdn: string; + gateway: string; + login?: string; + }; + contactInfo?: { + discord?: string; + github?: string; + email?: string; + spacebar?: string; + matrix?: string; + mastodon?: string; + }; +}; +const instances = JSON.parse( + readFileSync(process.env.JANK_INSTANCES_PATH || __dirname + "/webpage/instances.json").toString(), +) as instace[]; const instanceNames = new Map(); -for(const instance of instances){ +for (const instance of instances) { instanceNames.set(instance.name, instance); } app.use(compression()); -async function updateInstances(): Promise{ - try{ - const response = await fetch("https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/instances/instances.json"); +async function updateInstances(): Promise { + try { + const response = await fetch( + "https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/instances/instances.json", + ); const json = (await response.json()) as Instance[]; - for(const instance of json){ - if(instanceNames.has(instance.name)){ + for (const instance of json) { + if (instanceNames.has(instance.name)) { const existingInstance = instanceNames.get(instance.name); - if(existingInstance){ - for(const key of Object.keys(instance)){ - if(!existingInstance[key]){ + if (existingInstance) { + for (const key of Object.keys(instance)) { + if (!existingInstance[key]) { existingInstance[key] = instance[key]; } } } - }else{ + } else { instances.push(instance as any); } } observe(instances); - }catch(error){ + } catch (error) { console.error("Error updating instances:", error); } } updateInstances(); -app.use("/getupdates", async (_req: Request, res: Response)=>{ - try{ +app.use("/getupdates", async (_req: Request, res: Response) => { + try { const stats = await fs.stat(path.join(__dirname, "webpage")); res.send(stats.mtimeMs.toString()); - }catch(error){ + } catch (error) { console.error("Error getting updates:", error); res.status(500).send("Error getting updates"); } }); -app.use("/services/oembed", (req: Request, res: Response)=>{ +app.use("/services/oembed", (req: Request, res: Response) => { inviteResponse(req, res); }); -app.use("/uptime", (req: Request, res: Response)=>{ +app.use("/uptime", (req: Request, res: Response) => { const instanceUptime = uptime.get(req.query.name as string); res.send(instanceUptime); }); -app.use("/", async (req: Request, res: Response)=>{ +app.use("/", async (req: Request, res: Response) => { const scheme = req.secure ? "https" : "http"; const host = `${scheme}://${req.get("Host")}`; const ref = host + req.originalUrl; - if(host && ref){ + if (host && ref) { const link = `${host}/services/oembed?url=${encodeURIComponent(ref)}`; res.set( "Link", - `<${link}>; rel="alternate"; type="application/json+oembed"; title="Jank Client oEmbed format"` + `<${link}>; rel="alternate"; type="application/json+oembed"; title="Jank Client oEmbed format"`, ); } - if(req.path === "/"){ + if (req.path === "/") { res.sendFile(path.join(__dirname, "webpage", "home.html")); return; } - if(req.path.startsWith("/instances.json")){ + if (req.path.startsWith("/instances.json")) { res.json(instances); return; } - if(req.path.startsWith("/invite/")){ + if (req.path.startsWith("/invite/")) { res.sendFile(path.join(__dirname, "webpage", "invite.html")); return; } const filePath = path.join(__dirname, "webpage", req.path); - try{ + try { await fs.access(filePath); - if(devmode){ + if (devmode) { const filePath2 = path.join(__dirname, "../src/webpage", req.path); - try{ + try { await fs.access(filePath2); res.sendFile(filePath2); return; - }catch{} + } catch {} } res.sendFile(filePath); - }catch{ - try{ + } catch { + try { await fs.access(`${filePath}.html`); - if(devmode){ + if (devmode) { const filePath2 = path.join(__dirname, "../src/webpage", req.path); - try{ + try { await fs.access(filePath2 + ".html"); res.sendFile(filePath2 + ".html"); return; - }catch{} + } catch {} } res.sendFile(`${filePath}.html`); - }catch{ - if(req.path.startsWith("/src/webpage")){ + } catch { + if (req.path.startsWith("/src/webpage")) { const filePath2 = path.join(__dirname, "..", req.path); - try{ + try { await fs.access(filePath2); res.sendFile(filePath2); return; - }catch{} + } catch {} } res.sendFile(path.join(__dirname, "webpage", "index.html")); } } }); -app.set('trust proxy', (ip:string) => ip.startsWith("127.")); +app.set("trust proxy", (ip: string) => ip.startsWith("127.")); const PORT = process.env.PORT || Number(process.argv[2]) || 8080; -app.listen(PORT, ()=>{ +app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); }); -export{ getApiUrls }; +export {getApiUrls}; diff --git a/src/stats.ts b/src/stats.ts index 1356d30..d26062b 100644 --- a/src/stats.ts +++ b/src/stats.ts @@ -1,39 +1,39 @@ -import fs from"node:fs"; -import path from"node:path"; -import{ getApiUrls }from"./utils.js"; -import{ fileURLToPath }from"node:url"; -import{ setTimeout, clearTimeout }from"node:timers"; +import fs from "node:fs"; +import path from "node:path"; +import {getApiUrls} from "./utils.js"; +import {fileURLToPath} from "node:url"; +import {setTimeout, clearTimeout} from "node:timers"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); interface UptimeEntry { - time: number; - online: boolean; + time: number; + online: boolean; } interface Instance { - name: string; - urls?: { api: string }; - url?: string; - online?: boolean; - uptime?: { - daytime: number; - weektime: number; - alltime: number; - }; + name: string; + urls?: {api: string}; + url?: string; + online?: boolean; + uptime?: { + daytime: number; + weektime: number; + alltime: number; + }; } const uptimeObject: Map = loadUptimeObject(); -export{ uptimeObject as uptime }; +export {uptimeObject as uptime}; -function loadUptimeObject(): Map{ - const filePath = process.env.JANK_UPTIME_JSON_PATH||path.join(__dirname, "..", "uptime.json"); - if(fs.existsSync(filePath)){ - try{ +function loadUptimeObject(): Map { + const filePath = process.env.JANK_UPTIME_JSON_PATH || path.join(__dirname, "..", "uptime.json"); + if (fs.existsSync(filePath)) { + try { const data = JSON.parse(fs.readFileSync(filePath, "utf8")); return new Map(Object.entries(data)); - }catch(error){ + } catch (error) { console.error("Error reading uptime.json:", error); return new Map(); } @@ -43,26 +43,26 @@ function loadUptimeObject(): Map{ let saveTimeout: ReturnType | null = null; -function saveUptimeObject(): void{ - if(saveTimeout){ +function saveUptimeObject(): void { + if (saveTimeout) { clearTimeout(saveTimeout); } - saveTimeout = setTimeout(()=>{ + saveTimeout = setTimeout(() => { const data = Object.fromEntries(uptimeObject); fs.writeFile( - process.env.JANK_UPTIME_JSON_PATH||path.join(__dirname, "..", "uptime.json"), + process.env.JANK_UPTIME_JSON_PATH || path.join(__dirname, "..", "uptime.json"), JSON.stringify(data), - error=>{ - if(error){ + (error) => { + if (error) { console.error("Error saving uptime.json:", error); } - } + }, ); }, 5000); // Batch updates every 5 seconds } -function removeUndefinedKey(): void{ - if(uptimeObject.has("undefined")){ +function removeUndefinedKey(): void { + if (uptimeObject.has("undefined")) { uptimeObject.delete("undefined"); saveUptimeObject(); } @@ -70,101 +70,89 @@ function removeUndefinedKey(): void{ removeUndefinedKey(); -export async function observe(instances: Instance[]): Promise{ +export async function observe(instances: Instance[]): Promise { const activeInstances = new Set(); - const instancePromises = instances.map(instance=>resolveInstance(instance, activeInstances) - ); + const instancePromises = instances.map((instance) => resolveInstance(instance, activeInstances)); await Promise.allSettled(instancePromises); updateInactiveInstances(activeInstances); } -async function resolveInstance( - instance: Instance, - activeInstances: Set -): Promise{ - try{ +async function resolveInstance(instance: Instance, activeInstances: Set): Promise { + try { calcStats(instance); const api = await getApiUrl(instance); - if(!api){ + if (!api) { handleUnresolvedApi(instance); return; } activeInstances.add(instance.name); await checkHealth(instance, api); scheduleHealthCheck(instance, api); - }catch(error){ + } catch (error) { console.error("Error resolving instance:", error); } } -async function getApiUrl(instance: Instance): Promise{ - if(instance.urls){ +async function getApiUrl(instance: Instance): Promise { + if (instance.urls) { return instance.urls.api; } - if(instance.url){ + if (instance.url) { const urls = await getApiUrls(instance.url); return urls ? urls.api : null; } return null; } -function handleUnresolvedApi(instance: Instance): void{ +function handleUnresolvedApi(instance: Instance): void { setStatus(instance, false); console.warn(`${instance.name} does not resolve api URL`, instance); - setTimeout(()=>resolveInstance(instance, new Set()), 1000 * 60 * 30); + setTimeout(() => resolveInstance(instance, new Set()), 1000 * 60 * 30); } -function scheduleHealthCheck(instance: Instance, api: string): void{ +function scheduleHealthCheck(instance: Instance, api: string): void { const checkInterval = 1000 * 60 * 30; const initialDelay = Math.random() * 1000 * 60 * 10; - setTimeout(()=>{ + setTimeout(() => { checkHealth(instance, api); - setInterval(()=>checkHealth(instance, api), checkInterval); + setInterval(() => checkHealth(instance, api), checkInterval); }, initialDelay); } -async function checkHealth( - instance: Instance, - api: string, - tries = 0 -): Promise{ - try{ - const response = await fetch(`${api}/ping`, { method: "HEAD" }); +async function checkHealth(instance: Instance, api: string, tries = 0): Promise { + try { + const response = await fetch(`${api}/ping`, {method: "HEAD"}); console.log(`Checking health for ${instance.name}: ${response.status}`); - if(response.ok || tries > 3){ + if (response.ok || tries > 3) { setStatus(instance, response.ok); - }else{ + } else { retryHealthCheck(instance, api, tries); } - }catch(error){ + } catch (error) { console.error(`Error checking health for ${instance.name}:`, error); - if(tries > 3){ + if (tries > 3) { setStatus(instance, false); - }else{ + } else { retryHealthCheck(instance, api, tries); } } } -function retryHealthCheck( - instance: Instance, - api: string, - tries: number -): void{ - setTimeout(()=>checkHealth(instance, api, tries + 1), 30000); +function retryHealthCheck(instance: Instance, api: string, tries: number): void { + setTimeout(() => checkHealth(instance, api, tries + 1), 30000); } -function updateInactiveInstances(activeInstances: Set): void{ - for(const key of uptimeObject.keys()){ - if(!activeInstances.has(key)){ +function updateInactiveInstances(activeInstances: Set): void { + for (const key of uptimeObject.keys()) { + if (!activeInstances.has(key)) { setStatus(key, false); } } } -function calcStats(instance: Instance): void{ +function calcStats(instance: Instance): void { const obj = uptimeObject.get(instance.name); - if(!obj)return; + if (!obj) return; const now = Date.now(); const day = now - 1000 * 60 * 60 * 24; @@ -176,7 +164,7 @@ function calcStats(instance: Instance): void{ let weektime = 0; let online = false; - for(let i = 0; i < obj.length; i++){ + for (let i = 0; i < obj.length; i++) { const entry = obj[i]; online = entry.online; const stamp = entry.time; @@ -186,11 +174,11 @@ function calcStats(instance: Instance): void{ totalTimePassed += timePassed; alltime += Number(online) * timePassed; - if(stamp + timePassed > week){ + if (stamp + timePassed > week) { const weekTimePassed = Math.min(timePassed, nextStamp - week); weektime += Number(online) * weekTimePassed; - if(stamp + timePassed > day){ + if (stamp + timePassed > day) { const dayTimePassed = Math.min(weekTimePassed, nextStamp - day); daytime += Number(online) * dayTimePassed; } @@ -198,13 +186,7 @@ function calcStats(instance: Instance): void{ } instance.online = online; - instance.uptime = calculateUptimeStats( - totalTimePassed, - alltime, - daytime, - weektime, - online - ); + instance.uptime = calculateUptimeStats(totalTimePassed, alltime, daytime, weektime, online); } function calculateUptimeStats( @@ -212,46 +194,46 @@ function calculateUptimeStats( alltime: number, daytime: number, weektime: number, - online: boolean -): { daytime: number; weektime: number; alltime: number }{ + online: boolean, +): {daytime: number; weektime: number; alltime: number} { const dayInMs = 1000 * 60 * 60 * 24; const weekInMs = dayInMs * 7; alltime /= totalTimePassed; - if(totalTimePassed > dayInMs){ + if (totalTimePassed > dayInMs) { daytime = daytime || (online ? dayInMs : 0); daytime /= dayInMs; - if(totalTimePassed > weekInMs){ + if (totalTimePassed > weekInMs) { weektime = weektime || (online ? weekInMs : 0); weektime /= weekInMs; - }else{ + } else { weektime = alltime; } - }else{ + } else { weektime = alltime; daytime = alltime; } - return{ daytime, weektime, alltime }; + return {daytime, weektime, alltime}; } -function setStatus(instance: string | Instance, status: boolean): void{ +function setStatus(instance: string | Instance, status: boolean): void { const name = typeof instance === "string" ? instance : instance.name; let obj = uptimeObject.get(name); - if(!obj){ + if (!obj) { obj = []; uptimeObject.set(name, obj); } const lastEntry = obj.at(-1); - if(!lastEntry || lastEntry.online !== status){ - obj.push({ time: Date.now(), online: status }); + if (!lastEntry || lastEntry.online !== status) { + obj.push({time: Date.now(), online: status}); saveUptimeObject(); - if(typeof instance !== "string"){ + if (typeof instance !== "string") { calcStats(instance); } } diff --git a/src/utils.ts b/src/utils.ts index 029a13c..b553fa1 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,74 +1,76 @@ -import{ Request, Response }from"express"; +import {Request, Response} from "express"; interface ApiUrls { - api: string; - gateway: string; - cdn: string; - wellknown: string; + api: string; + gateway: string; + cdn: string; + wellknown: string; } interface Invite { - guild: { - name: string; - description?: string; - icon?: string; - id: string; - }; - inviter?: { - username: string; - }; + guild: { + name: string; + description?: string; + icon?: string; + id: string; + }; + inviter?: { + username: string; + }; } -export async function getApiUrls(url: string): Promise{ - if(!url.endsWith("/")){ +export async function getApiUrls(url: string): Promise { + if (!url.endsWith("/")) { url += "/"; } - try{ - const info: ApiUrls = await fetch(`${url}.well-known/spacebar`).then(res=>res.json()); + try { + const info: ApiUrls = await fetch(`${url}.well-known/spacebar`).then((res) => res.json()); const api = info.api; const apiUrl = new URL(api); const policies: any = await fetch( - `${api}${apiUrl.pathname.includes("api") ? "" : "api"}/policies/instance/domains` - ).then(res=>res.json()); - return{ + `${api}${apiUrl.pathname.includes("api") ? "" : "api"}/policies/instance/domains`, + ).then((res) => res.json()); + return { api: policies.apiEndpoint, gateway: policies.gateway, cdn: policies.cdn, wellknown: url, }; - }catch(error){ + } catch (error) { console.error("Error fetching API URLs:", error); return null; } } -export async function inviteResponse(req: Request, res: Response): Promise{ +export async function inviteResponse(req: Request, res: Response): Promise { let url: URL; - try{ + try { url = new URL(req.query.url as string); - }catch{ + } catch { const scheme = req.secure ? "https" : "http"; const host = `${scheme}://${req.get("Host")}`; url = new URL(host); } - try{ - if(url.pathname.startsWith("invite")){ + try { + if (url.pathname.startsWith("invite")) { throw new Error("Invalid invite URL"); } const code = url.pathname.split("/")[2]; const instance = url.searchParams.get("instance"); - if(!instance){ + if (!instance) { throw new Error("Instance not specified"); } const urls = await getApiUrls(instance); - if(!urls){ + if (!urls) { throw new Error("Failed to get API URLs"); } - const invite = await fetch(`${urls.api}/invites/${code}`).then(json=>json.json() as Promise); + const invite = await fetch(`${urls.api}/invites/${code}`).then( + (json) => json.json() as Promise, + ); const title = invite.guild.name; const description = invite.inviter ? `${invite.inviter.username} has invited you to ${invite.guild.name}${invite.guild.description ? `\n${invite.guild.description}` : ""}` @@ -84,7 +86,7 @@ export async function inviteResponse(req: Request, res: Response): Promise thumbnail, description, }); - }catch(error){ + } catch (error) { console.error("Error processing invite response:", error); res.json({ type: "link", @@ -95,4 +97,4 @@ export async function inviteResponse(req: Request, res: Response): Promise url: url.toString(), }); } -} \ No newline at end of file +} diff --git a/src/webpage/audio/audio.ts b/src/webpage/audio/audio.ts index d69a8c9..ecebd35 100644 --- a/src/webpage/audio/audio.ts +++ b/src/webpage/audio/audio.ts @@ -1,34 +1,34 @@ -import { BinRead } from "../utils/binaryUtils.js"; -import { Track } from "./track.js"; +import {BinRead} from "../utils/binaryUtils.js"; +import {Track} from "./track.js"; -export class Audio{ - name:string; - tracks:(Track|number)[]; - constructor(name:string,tracks:(Track|number)[]){ - this.tracks=tracks; - this.name=name; - } - static parse(read:BinRead,trackarr:Track[]):Audio{ - const name=read.readString8(); - const length=read.read16(); - const tracks:(Track|number)[]=[] - for(let i=0;isetTimeout(res,thing)); - } - } - } +export class Audio { + name: string; + tracks: (Track | number)[]; + constructor(name: string, tracks: (Track | number)[]) { + this.tracks = tracks; + this.name = name; + } + static parse(read: BinRead, trackarr: Track[]): Audio { + const name = read.readString8(); + const length = read.read16(); + const tracks: (Track | number)[] = []; + for (let i = 0; i < length; i++) { + let index = read.read16(); + if (index === 0) { + tracks.push(read.readFloat32()); + } else { + tracks.push(trackarr[index - 1]); + } + } + return new Audio(name, tracks); + } + async play() { + for (const thing of this.tracks) { + if (thing instanceof Track) { + thing.play(); + } else { + await new Promise((res) => setTimeout(res, thing)); + } + } + } } diff --git a/src/webpage/audio/index.html b/src/webpage/audio/index.html index 1d71d8f..360beb5 100644 --- a/src/webpage/audio/index.html +++ b/src/webpage/audio/index.html @@ -1,26 +1,39 @@ - + - - - + + Jank Audio - - - - - - - + + + + + + + - +

This will eventually be something

-

I want to let the sound system of jank not be so hard coded, but I still need to work on everything a bit before that can happen. Thanks for your patience.

+

+ I want to let the sound system of jank not be so hard coded, but I still need to work on + everything a bit before that can happen. Thanks for your patience. +

why does this tool need to exist?

-

For size reasons jank does not use normal sound files, so I need to make this whole format to be more adaptable

+

+ For size reasons jank does not use normal sound files, so I need to make this whole format to + be more adaptable +

- diff --git a/src/webpage/audio/page.ts b/src/webpage/audio/page.ts index ccc5d45..8905bd5 100644 --- a/src/webpage/audio/page.ts +++ b/src/webpage/audio/page.ts @@ -1,9 +1,9 @@ -import { BinWrite } from "../utils/binaryUtils.js"; -import { setTheme } from "../utils/utils.js"; -import { Play } from "./play.js"; +import {BinWrite} from "../utils/binaryUtils.js"; +import {setTheme} from "../utils/utils.js"; +import {Play} from "./play.js"; setTheme(); -const w=new BinWrite(2**12); +const w = new BinWrite(2 ** 12); w.writeStringNo("jasf"); w.write8(4); @@ -18,100 +18,103 @@ w.writeString8("custom"); w.write32Float(150); //return Math.sin(((t + 2) ** Math.cos(t * 4)) * Math.PI * 2 * freq); //Math.sin((((t+2)**Math.cos((t*4)))*((Math.PI*2)*f))) -w.write8(4);//sin -w.write8(5)//times +w.write8(4); //sin +w.write8(5); //times { - w.write8(9);//Power + w.write8(9); //Power - { - w.write8(6);//adding - w.write8(1);//t - w.write8(0);w.write32Float(2);//2 - } - w.write8(13);//cos - w.write8(5);// times - w.write8(1);//t - w.write8(0);w.write32Float(4);//4 + { + w.write8(6); //adding + w.write8(1); //t + w.write8(0); + w.write32Float(2); //2 + } + w.write8(13); //cos + w.write8(5); // times + w.write8(1); //t + w.write8(0); + w.write32Float(4); //4 } { - w.write8(5)//times - w.write8(5)//times - w.write8(3);//PI - w.write8(0);w.write32Float(2);//2 - w.write8(2);//freq + w.write8(5); //times + w.write8(5); //times + w.write8(3); //PI + w.write8(0); + w.write32Float(2); //2 + w.write8(2); //freq } -w.write16(4);//3 tracks +w.write16(4); //3 tracks -w.write16(1);//zip +w.write16(1); //zip w.write8(4); -w.write32Float(1) -w.write32Float(700) +w.write32Float(1); +w.write32Float(700); -w.write16(3);//beep +w.write16(3); //beep { - w.write8(1); - w.write32Float(1) - w.write32Float(700); - w.write32Float(50); + w.write8(1); + w.write32Float(1); + w.write32Float(700); + w.write32Float(50); - w.write8(0); - w.write32Float(100); + w.write8(0); + w.write32Float(100); - w.write8(1); - w.write32Float(1) - w.write32Float(700); - w.write32Float(50); + w.write8(1); + w.write32Float(1); + w.write32Float(700); + w.write32Float(50); } -w.write16(5);//three +w.write16(5); //three { - w.write8(1); - w.write32Float(1) - w.write32Float(800); - w.write32Float(50); + w.write8(1); + w.write32Float(1); + w.write32Float(800); + w.write32Float(50); - w.write8(0); - w.write32Float(50); + w.write8(0); + w.write32Float(50); - w.write8(1); - w.write32Float(1) - w.write32Float(1000); - w.write32Float(50); + w.write8(1); + w.write32Float(1); + w.write32Float(1000); + w.write32Float(50); - w.write8(0); - w.write32Float(50); + w.write8(0); + w.write32Float(50); - w.write8(1); - w.write32Float(1) - w.write32Float(1300); - w.write32Float(50); + w.write8(1); + w.write32Float(1); + w.write32Float(1300); + w.write32Float(50); } -w.write16(5);//square +w.write16(5); //square { - w.write8(3); - w.write32Float(1) - w.write32Float(600); - w.write32Float(50); + w.write8(3); + w.write32Float(1); + w.write32Float(600); + w.write32Float(50); - w.write8(0); - w.write32Float(50); + w.write8(0); + w.write32Float(50); - w.write8(3); - w.write32Float(1) - w.write32Float(800); - w.write32Float(50); + w.write8(3); + w.write32Float(1); + w.write32Float(800); + w.write32Float(50); - w.write8(0); - w.write32Float(50); + w.write8(0); + w.write32Float(50); - w.write8(3); - w.write32Float(1) - w.write32Float(1000); - w.write32Float(50); + w.write8(3); + w.write32Float(1); + w.write32Float(1000); + w.write32Float(50); } -w.write16(4);//2 audio +w.write16(4); //2 audio w.writeString8("zip"); w.write16(1); @@ -128,8 +131,8 @@ w.write16(3); w.writeString8("square"); w.write16(1); w.write16(4); -const buff=w.getBuffer(); -const play=Play.parseBin(buff); +const buff = w.getBuffer(); +const play = Play.parseBin(buff); /* const zip=play.audios.get("square"); if(zip){ @@ -140,18 +143,18 @@ if(zip){ console.log(play.voices[3][0].info.wave) }; */ -console.log(play,buff); +console.log(play, buff); -const download=document.getElementById("download"); -if(download){ - download.onclick=()=>{ - const blob = new Blob([buff], { type: "binary" }); - const downloadUrl = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = downloadUrl; - a.download = "sounds.jasf"; - document.body.appendChild(a); - a.click(); - URL.revokeObjectURL(downloadUrl); - } +const download = document.getElementById("download"); +if (download) { + download.onclick = () => { + const blob = new Blob([buff], {type: "binary"}); + const downloadUrl = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = downloadUrl; + a.download = "sounds.jasf"; + document.body.appendChild(a); + a.click(); + URL.revokeObjectURL(downloadUrl); + }; } diff --git a/src/webpage/audio/play.ts b/src/webpage/audio/play.ts index 8ae81f9..eb6adca 100644 --- a/src/webpage/audio/play.ts +++ b/src/webpage/audio/play.ts @@ -1,48 +1,48 @@ -import { BinRead } from "../utils/binaryUtils.js"; -import { Track } from "./track.js"; -import { AVoice } from "./voice.js"; -import { Audio } from "./audio.js"; -export class Play{ - voices:[AVoice,string][] - tracks:Track[] - audios:Map; - constructor(voices:[AVoice,string][],tracks:Track[],audios:Map){ - this.voices=voices; - this.tracks=tracks; - this.audios=audios; - } - static parseBin(buffer:ArrayBuffer){ - const read=new BinRead(buffer); - if(read.readStringNo(4)!=="jasf") throw new Error("this is not a jasf file"); - let voices=read.read8(); - let six=false; - if(voices===255){ - voices=read.read16(); - six=true; - } - const voiceArr:[AVoice,string][]=[]; - for(let i=0;i; + constructor(voices: [AVoice, string][], tracks: Track[], audios: Map) { + this.voices = voices; + this.tracks = tracks; + this.audios = audios; + } + static parseBin(buffer: ArrayBuffer) { + const read = new BinRead(buffer); + if (read.readStringNo(4) !== "jasf") throw new Error("this is not a jasf file"); + let voices = read.read8(); + let six = false; + if (voices === 255) { + voices = read.read16(); + six = true; + } + const voiceArr: [AVoice, string][] = []; + for (let i = 0; i < voices; i++) { + voiceArr.push(AVoice.getVoice(read)); + } - const tracks=read.read16(); - const trackArr:Track[]=[]; - for(let i=0;i(); - for(let i=0;i(); + for (let i = 0; i < audios; i++) { + const a = Audio.parse(read, trackArr); + audioArr.set(a.name, a); + } - return new Play(voiceArr,trackArr,audioArr); - } - static async playURL(url:string){ - const res=await fetch(url); - const arr=await res.arrayBuffer(); - return this.parseBin(arr); - } + return new Play(voiceArr, trackArr, audioArr); + } + static async playURL(url: string) { + const res = await fetch(url); + const arr = await res.arrayBuffer(); + return this.parseBin(arr); + } } diff --git a/src/webpage/audio/track.ts b/src/webpage/audio/track.ts index dac4af3..0e20144 100644 --- a/src/webpage/audio/track.ts +++ b/src/webpage/audio/track.ts @@ -1,46 +1,45 @@ -import { BinRead } from "../utils/binaryUtils.js"; -import { AVoice } from "./voice.js"; +import {BinRead} from "../utils/binaryUtils.js"; +import {AVoice} from "./voice.js"; -export class Track{ - seq:(AVoice|number)[]; - constructor(playing:(AVoice|number)[]){ - this.seq=playing; - } - static parse(read:BinRead,play:[AVoice,string][],six:boolean):Track{ - const length=read.read16(); - const play2:(AVoice|number)[]=[]; - for(let i=0;isetTimeout(res,thing)); - } - } - } +export class Track { + seq: (AVoice | number)[]; + constructor(playing: (AVoice | number)[]) { + this.seq = playing; + } + static parse(read: BinRead, play: [AVoice, string][], six: boolean): Track { + const length = read.read16(); + const play2: (AVoice | number)[] = []; + for (let i = 0; i < length; i++) { + let index: number; + if (six) { + index = read.read16(); + } else { + index = read.read8(); + } + if (index === 0) { + play2.push(read.readFloat32()); + continue; + } + index--; + if (!play[index]) throw new Error("voice not found"); + const [voice] = play[index]; + let temp: AVoice; + if (voice.info.wave instanceof Function) { + temp = voice.clone(read.readFloat32(), read.readFloat32()); + } else { + temp = voice.clone(read.readFloat32(), read.readFloat32(), read.readFloat32()); + } + play2.push(temp); + } + return new Track(play2); + } + async play() { + for (const thing of this.seq) { + if (thing instanceof AVoice) { + thing.playL(); + } else { + await new Promise((res) => setTimeout(res, thing)); + } + } + } } diff --git a/src/webpage/audio/voice.ts b/src/webpage/audio/voice.ts index 703fa12..69f1287 100644 --- a/src/webpage/audio/voice.ts +++ b/src/webpage/audio/voice.ts @@ -1,23 +1,23 @@ -import { BinRead } from "../utils/binaryUtils.js"; +import {BinRead} from "../utils/binaryUtils.js"; -class AVoice{ +class AVoice { audioCtx: AudioContext; - info: { wave: string | Function; freq: number }; + info: {wave: string | Function; freq: number}; playing: boolean; myArrayBuffer: AudioBuffer; gainNode: GainNode; buffer: Float32Array; source: AudioBufferSourceNode; - length=1; - constructor(wave: string | Function, freq: number, volume = 1,length=1000){ - this.length=length; + length = 1; + constructor(wave: string | Function, freq: number, volume = 1, length = 1000) { + this.length = length; this.audioCtx = new window.AudioContext(); - this.info = { wave, freq }; + this.info = {wave, freq}; this.playing = false; this.myArrayBuffer = this.audioCtx.createBuffer( 1, - this.audioCtx.sampleRate*length/1000, - this.audioCtx.sampleRate + (this.audioCtx.sampleRate * length) / 1000, + this.audioCtx.sampleRate, ); this.gainNode = this.audioCtx.createGain(); this.gainNode.gain.value = volume; @@ -29,193 +29,193 @@ class AVoice{ this.source.start(); this.updateWave(); } - clone(volume:number,freq:number,length=this.length){ - return new AVoice(this.wave,freq,volume,length); + clone(volume: number, freq: number, length = this.length) { + return new AVoice(this.wave, freq, volume, length); } - get wave(): string | Function{ + get wave(): string | Function { return this.info.wave; } - get freq(): number{ + get freq(): number { return this.info.freq; } - set wave(wave: string | Function){ + set wave(wave: string | Function) { this.info.wave = wave; this.updateWave(); } - set freq(freq: number){ + set freq(freq: number) { this.info.freq = freq; this.updateWave(); } - updateWave(): void{ + updateWave(): void { const func = this.waveFunction(); - for(let i = 0; i < this.buffer.length; i++){ + for (let i = 0; i < this.buffer.length; i++) { this.buffer[i] = func(i / this.audioCtx.sampleRate, this.freq); } } - waveFunction(): Function{ - if(typeof this.wave === "function"){ + waveFunction(): Function { + if (typeof this.wave === "function") { return this.wave; } - switch(this.wave){ - case"sin": - return(t: number, freq: number)=>{ - return Math.sin(t * Math.PI * 2 * freq); - }; - case"triangle": - return(t: number, freq: number)=>{ - return Math.abs(((4 * t * freq) % 4) - 2) - 1; - }; - case"sawtooth": - return(t: number, freq: number)=>{ - return((t * freq) % 1) * 2 - 1; - }; - case"square": - return(t: number, freq: number)=>{ - return(t * freq) % 2 < 1 ? 1 : -1; - }; - case"white": - return(_t: number, _freq: number)=>{ - return Math.random() * 2 - 1; - }; + switch (this.wave) { + case "sin": + return (t: number, freq: number) => { + return Math.sin(t * Math.PI * 2 * freq); + }; + case "triangle": + return (t: number, freq: number) => { + return Math.abs(((4 * t * freq) % 4) - 2) - 1; + }; + case "sawtooth": + return (t: number, freq: number) => { + return ((t * freq) % 1) * 2 - 1; + }; + case "square": + return (t: number, freq: number) => { + return (t * freq) % 2 < 1 ? 1 : -1; + }; + case "white": + return (_t: number, _freq: number) => { + return Math.random() * 2 - 1; + }; } return new Function(); } - play(): void{ - if(this.playing){ + play(): void { + if (this.playing) { return; } this.source.connect(this.gainNode); this.playing = true; } - playL(){ + playL() { this.play(); - setTimeout(()=>{ + setTimeout(() => { this.stop(); - },this.length); + }, this.length); } - stop(): void{ - if(this.playing){ + stop(): void { + if (this.playing) { this.source.disconnect(); this.playing = false; } } - static noises(noise: string): void{ - switch(noise){ - case"three": { - const voicy = new AVoice("sin", 800); - voicy.play(); - setTimeout(_=>{ - voicy.freq = 1000; - }, 50); - setTimeout(_=>{ - voicy.freq = 1300; - }, 100); - setTimeout(_=>{ - voicy.stop(); - }, 150); - break; - } - case"zip": { - const voicy = new AVoice((t: number, freq: number)=>{ - return Math.sin((t + 2) ** Math.cos(t * 4) * Math.PI * 2 * freq); - }, 700); - voicy.play(); - setTimeout(_=>{ - voicy.stop(); - }, 150); - break; - } - case"square": { - const voicy = new AVoice("square", 600, 0.4); - voicy.play(); - setTimeout(_=>{ - voicy.freq = 800; - }, 50); - setTimeout(_=>{ - voicy.freq = 1000; - }, 100); - setTimeout(_=>{ - voicy.stop(); - }, 150); - break; - } - case"beep": { - const voicy = new AVoice("sin", 800); - voicy.play(); - setTimeout(_=>{ - voicy.stop(); - }, 50); - setTimeout(_=>{ + static noises(noise: string): void { + switch (noise) { + case "three": { + const voicy = new AVoice("sin", 800); voicy.play(); - }, 100); - setTimeout(_=>{ - voicy.stop(); - }, 150); - break; - } - case "join":{ - const voicy = new AVoice("triangle", 600,.1); - voicy.play(); - setTimeout(_=>{ - voicy.freq=800; - }, 75); - setTimeout(_=>{ - voicy.freq=1000; - }, 150); - setTimeout(_=>{ - voicy.stop(); - }, 200); - break; - } - case "leave":{ - const voicy = new AVoice("triangle", 850,.5); - voicy.play(); - setTimeout(_=>{ - voicy.freq=700; - }, 100); - setTimeout(_=>{ - voicy.stop(); - voicy.freq=400; - }, 180); - setTimeout(_=>{ + setTimeout((_) => { + voicy.freq = 1000; + }, 50); + setTimeout((_) => { + voicy.freq = 1300; + }, 100); + setTimeout((_) => { + voicy.stop(); + }, 150); + break; + } + case "zip": { + const voicy = new AVoice((t: number, freq: number) => { + return Math.sin((t + 2) ** Math.cos(t * 4) * Math.PI * 2 * freq); + }, 700); voicy.play(); - }, 200); - setTimeout(_=>{ - voicy.stop(); - }, 250); - break; - } + setTimeout((_) => { + voicy.stop(); + }, 150); + break; + } + case "square": { + const voicy = new AVoice("square", 600, 0.4); + voicy.play(); + setTimeout((_) => { + voicy.freq = 800; + }, 50); + setTimeout((_) => { + voicy.freq = 1000; + }, 100); + setTimeout((_) => { + voicy.stop(); + }, 150); + break; + } + case "beep": { + const voicy = new AVoice("sin", 800); + voicy.play(); + setTimeout((_) => { + voicy.stop(); + }, 50); + setTimeout((_) => { + voicy.play(); + }, 100); + setTimeout((_) => { + voicy.stop(); + }, 150); + break; + } + case "join": { + const voicy = new AVoice("triangle", 600, 0.1); + voicy.play(); + setTimeout((_) => { + voicy.freq = 800; + }, 75); + setTimeout((_) => { + voicy.freq = 1000; + }, 150); + setTimeout((_) => { + voicy.stop(); + }, 200); + break; + } + case "leave": { + const voicy = new AVoice("triangle", 850, 0.5); + voicy.play(); + setTimeout((_) => { + voicy.freq = 700; + }, 100); + setTimeout((_) => { + voicy.stop(); + voicy.freq = 400; + }, 180); + setTimeout((_) => { + voicy.play(); + }, 200); + setTimeout((_) => { + voicy.stop(); + }, 250); + break; + } } } - static get sounds(){ - return["three", "zip", "square", "beep"]; + static get sounds() { + return ["three", "zip", "square", "beep"]; } - static getVoice(read:BinRead):[AVoice,string]{ + static getVoice(read: BinRead): [AVoice, string] { const name = read.readString8(); - let length=read.readFloat32(); - let special:Function|string - if(length!==0){ - special=this.parseExpression(read); - }else{ - special=name; - length=1; + let length = read.readFloat32(); + let special: Function | string; + if (length !== 0) { + special = this.parseExpression(read); + } else { + special = name; + length = 1; } - return [new AVoice(special,0,0,length),name] + return [new AVoice(special, 0, 0, length), name]; } - static parseExpression(read:BinRead):Function{ - return new Function("t","f",`return ${this.PEHelper(read)};`); + static parseExpression(read: BinRead): Function { + return new Function("t", "f", `return ${this.PEHelper(read)};`); } - static PEHelper(read:BinRead):string{ - let state=read.read8(); - switch(state){ + static PEHelper(read: BinRead): string { + let state = read.read8(); + switch (state) { case 0: - return ""+read.readFloat32(); + return "" + read.readFloat32(); case 1: return "t"; case 2: return "f"; case 3: - return `Math.PI` + return `Math.PI`; case 4: return `Math.sin(${this.PEHelper(read)})`; case 5: @@ -238,9 +238,8 @@ class AVoice{ return `Math.cos(${this.PEHelper(read)})`; default: throw new Error("unexpected case found!"); - } } } -export{ AVoice as AVoice }; +export {AVoice as AVoice}; diff --git a/src/webpage/channel.ts b/src/webpage/channel.ts index 9b0d429..cd043d4 100644 --- a/src/webpage/channel.ts +++ b/src/webpage/channel.ts @@ -1,27 +1,34 @@ "use strict"; -import{ Message }from"./message.js"; -import{ AVoice }from"./audio/voice.js"; -import{ Contextmenu }from"./contextmenu.js"; -import{ Guild }from"./guild.js"; -import{ Localuser }from"./localuser.js"; -import{ Permissions }from"./permissions.js"; -import{ Dialog, Float, Settings }from"./settings.js"; -import{ Role, RoleList }from"./role.js"; -import{ InfiniteScroller }from"./infiniteScroller.js"; -import{ SnowFlake }from"./snowflake.js"; -import{channeljson,embedjson,messageCreateJson,messagejson,readyjson,startTypingjson}from"./jsontypes.js"; -import{ MarkDown }from"./markdown.js"; -import{ Member }from"./member.js"; -import { Voice } from "./voice.js"; -import { User } from "./user.js"; -import { I18n } from "./i18n.js"; +import {Message} from "./message.js"; +import {AVoice} from "./audio/voice.js"; +import {Contextmenu} from "./contextmenu.js"; +import {Guild} from "./guild.js"; +import {Localuser} from "./localuser.js"; +import {Permissions} from "./permissions.js"; +import {Dialog, Float, Settings} from "./settings.js"; +import {Role, RoleList} from "./role.js"; +import {InfiniteScroller} from "./infiniteScroller.js"; +import {SnowFlake} from "./snowflake.js"; +import { + channeljson, + embedjson, + messageCreateJson, + messagejson, + readyjson, + startTypingjson, +} from "./jsontypes.js"; +import {MarkDown} from "./markdown.js"; +import {Member} from "./member.js"; +import {Voice} from "./voice.js"; +import {User} from "./user.js"; +import {I18n} from "./i18n.js"; declare global { -interface NotificationOptions { -image?: string | null | undefined; + interface NotificationOptions { + image?: string | null | undefined; + } } -} -class Channel extends SnowFlake{ +class Channel extends SnowFlake { editing!: Message | null; type!: number; owner!: Guild; @@ -38,11 +45,11 @@ class Channel extends SnowFlake{ position: number = 0; lastreadmessageid: string | undefined; lastmessageid: string | undefined; - mentions=0; + mentions = 0; lastpin!: string; move_id?: string; typing!: number; - message_notifications:number=3; + message_notifications: number = 3; allthewayup!: boolean; static contextmenu = new Contextmenu("channel menu"); replyingto!: Message | null; @@ -50,60 +57,76 @@ class Channel extends SnowFlake{ idToPrev: Map = new Map(); idToNext: Map = new Map(); messages: Map = new Map(); - voice?:Voice; - bitrate:number=128000; + voice?: Voice; + bitrate: number = 128000; - muted:boolean=false; - mute_config= {selected_time_window: -1,end_time: 0} - handleUserOverrides(settings:{message_notifications: number,muted: boolean,mute_config: {selected_time_window: number,end_time: number},channel_id: string}){ - this.message_notifications=settings.message_notifications; - this.muted=settings.muted; - this.mute_config=settings.mute_config; + muted: boolean = false; + mute_config = {selected_time_window: -1, end_time: 0}; + handleUserOverrides(settings: { + message_notifications: number; + muted: boolean; + mute_config: {selected_time_window: number; end_time: number}; + channel_id: string; + }) { + this.message_notifications = settings.message_notifications; + this.muted = settings.muted; + this.mute_config = settings.mute_config; } - static setupcontextmenu(){ - this.contextmenu.addbutton(()=>I18n.getTranslation("channel.copyId"), function(this: Channel){ - navigator.clipboard.writeText(this.id); - }); - - this.contextmenu.addbutton(()=>I18n.getTranslation("channel.markRead"), function(this: Channel){ - this.readbottom(); - }); - - this.contextmenu.addbutton(()=>I18n.getTranslation("channel.settings"), function(this: Channel){ - this.generateSettings(); - },null,function(){ - return this.hasPermission("MANAGE_CHANNELS"); - }); + static setupcontextmenu() { + this.contextmenu.addbutton( + () => I18n.getTranslation("channel.copyId"), + function (this: Channel) { + navigator.clipboard.writeText(this.id); + }, + ); this.contextmenu.addbutton( - ()=>I18n.getTranslation("channel.delete"), - function(this: Channel){ + () => I18n.getTranslation("channel.markRead"), + function (this: Channel) { + this.readbottom(); + }, + ); + + this.contextmenu.addbutton( + () => I18n.getTranslation("channel.settings"), + function (this: Channel) { + this.generateSettings(); + }, + null, + function () { + return this.hasPermission("MANAGE_CHANNELS"); + }, + ); + + this.contextmenu.addbutton( + () => I18n.getTranslation("channel.delete"), + function (this: Channel) { this.deleteChannel(); }, null, - function(){ + function () { return this.isAdmin(); - } + }, ); this.contextmenu.addbutton( - ()=>I18n.getTranslation("guild.notifications"), - function(){ + () => I18n.getTranslation("guild.notifications"), + function () { this.setnotifcation(); - } - ) + }, + ); this.contextmenu.addbutton( - ()=>I18n.getTranslation("channel.makeInvite"), - function(this: Channel){ + () => I18n.getTranslation("channel.makeInvite"), + function (this: Channel) { this.createInvite(); }, null, - function(){ + function () { return this.hasPermission("CREATE_INSTANT_INVITE") && this.type !== 4; - } + }, ); } - createInvite(){ + createInvite() { const div = document.createElement("div"); div.classList.add("invitediv"); const text = document.createElement("span"); @@ -116,13 +139,13 @@ class Channel extends SnowFlake{ const copy = document.createElement("span"); copy.classList.add("copybutton", "svgicon", "svg-copy"); copycontainer.append(copy); - copycontainer.onclick = _=>{ - if(text.textContent){ + copycontainer.onclick = (_) => { + if (text.textContent) { navigator.clipboard.writeText(text.textContent); } }; div.append(copycontainer); - const update = ()=>{ + const update = () => { fetch(`${this.info.api}/channels/${this.id}/invites`, { method: "POST", headers: this.headers, @@ -135,8 +158,8 @@ class Channel extends SnowFlake{ temporary: uses !== 0, }), }) - .then(_=>_.json()) - .then(json=>{ + .then((_) => _.json()) + .then((json) => { const params = new URLSearchParams(""); params.set("instance", this.info.wellknown); const encoded = params.toString(); @@ -144,45 +167,75 @@ class Channel extends SnowFlake{ }); }; update(); - const inviteOptions=new Dialog("",{noSubmit:true}); + const inviteOptions = new Dialog("", {noSubmit: true}); inviteOptions.options.addTitle(I18n.getTranslation("inviteOptions.title")); - inviteOptions.options.addText(I18n.getTranslation("invite.subtext",this.name,this.guild.properties.name)); + inviteOptions.options.addText( + I18n.getTranslation("invite.subtext", this.name, this.guild.properties.name), + ); - inviteOptions.options.addSelect(I18n.getTranslation("invite.expireAfter"),()=>{}, - ["30m","1h","6h","12h","1d","7d","30d","never"].map((e)=>I18n.getTranslation("inviteOptions."+e)) - ).onchange=(e)=>{expires=[1800, 3600, 21600, 43200, 86400, 604800, 2592000, 0][e];update()}; + inviteOptions.options.addSelect( + I18n.getTranslation("invite.expireAfter"), + () => {}, + ["30m", "1h", "6h", "12h", "1d", "7d", "30d", "never"].map((e) => + I18n.getTranslation("inviteOptions." + e), + ), + ).onchange = (e) => { + expires = [1800, 3600, 21600, 43200, 86400, 604800, 2592000, 0][e]; + update(); + }; - const timeOptions=["1","5","10","25","50","100"].map((e)=>I18n.getTranslation("inviteOptions.limit",e)) - timeOptions.unshift(I18n.getTranslation("inviteOptions.noLimit")) - inviteOptions.options.addSelect(I18n.getTranslation("invite.expireAfter"),()=>{},timeOptions) - .onchange=(e)=>{uses=[0, 1, 5, 10, 25, 50, 100][e];update()}; + const timeOptions = ["1", "5", "10", "25", "50", "100"].map((e) => + I18n.getTranslation("inviteOptions.limit", e), + ); + timeOptions.unshift(I18n.getTranslation("inviteOptions.noLimit")); + inviteOptions.options.addSelect( + I18n.getTranslation("invite.expireAfter"), + () => {}, + timeOptions, + ).onchange = (e) => { + uses = [0, 1, 5, 10, 25, 50, 100][e]; + update(); + }; inviteOptions.options.addHTMLArea(div); inviteOptions.show(); } - generateSettings(){ + generateSettings() { this.sortPerms(); - const settings = new Settings(I18n.getTranslation("channel.settingsFor",this.name)); + const settings = new Settings(I18n.getTranslation("channel.settingsFor", this.name)); { - const gensettings=settings.addButton("Settings"); - const form=gensettings.addForm("",()=>{},{ - fetchURL:this.info.api + "/channels/" + this.id, + const gensettings = settings.addButton("Settings"); + const form = gensettings.addForm("", () => {}, { + fetchURL: this.info.api + "/channels/" + this.id, method: "PATCH", headers: this.headers, }); - form.addTextInput(I18n.getTranslation("channel.name:"),"name",{initText:this.name}); - form.addMDInput(I18n.getTranslation("channel.topic:"),"topic",{initText:this.topic}); - form.addCheckboxInput(I18n.getTranslation("channel.nsfw:"),"nsfw",{initState:this.nsfw}); - if(this.type!==4){ - const options=["voice", "text", "announcement"]; - form.addSelect("Type:","type",options.map(e=>I18n.getTranslation("channel."+e)),{ - defaultIndex:options.indexOf({0:"text", 2:"voice", 5:"announcement", 4:"category" }[this.type] as string) - },options); - form.addPreprocessor((obj:any)=>{ - obj.type={text: 0, voice: 2, announcement: 5, category: 4 }[obj.type as string] - }) + form.addTextInput(I18n.getTranslation("channel.name:"), "name", { + initText: this.name, + }); + form.addMDInput(I18n.getTranslation("channel.topic:"), "topic", { + initText: this.topic, + }); + form.addCheckboxInput(I18n.getTranslation("channel.nsfw:"), "nsfw", { + initState: this.nsfw, + }); + if (this.type !== 4) { + const options = ["voice", "text", "announcement"]; + form.addSelect( + "Type:", + "type", + options.map((e) => I18n.getTranslation("channel." + e)), + { + defaultIndex: options.indexOf( + {0: "text", 2: "voice", 5: "announcement", 4: "category"}[this.type] as string, + ), + }, + options, + ); + form.addPreprocessor((obj: any) => { + obj.type = {text: 0, voice: 2, announcement: 5, category: 4}[obj.type as string]; + }); } - } const s1 = settings.addButton("Permissions"); s1.options.push( @@ -190,78 +243,70 @@ class Channel extends SnowFlake{ this.permission_overwritesar, this.guild, this.updateRolePermissions.bind(this), - this - ) + this, + ), ); settings.show(); } - sortPerms(){ - this.permission_overwritesar.sort((a, b)=>{ - return( - this.guild.roles.indexOf(a[0]) - - this.guild.roles.indexOf(b[0]) - ); + sortPerms() { + this.permission_overwritesar.sort((a, b) => { + return this.guild.roles.indexOf(a[0]) - this.guild.roles.indexOf(b[0]); }); } - setUpInfiniteScroller(){ + setUpInfiniteScroller() { this.infinite = new InfiniteScroller( - async (id: string, offset: number): Promise=>{ - if(offset === 1){ - if(this.idToPrev.has(id)){ + async (id: string, offset: number): Promise => { + if (offset === 1) { + if (this.idToPrev.has(id)) { return this.idToPrev.get(id); - }else{ + } else { await this.grabBefore(id); return this.idToPrev.get(id); } - }else{ - if(this.idToNext.has(id)){ + } else { + if (this.idToNext.has(id)) { return this.idToNext.get(id); - }else if(this.lastmessage?.id !== id){ + } else if (this.lastmessage?.id !== id) { await this.grabAfter(id); return this.idToNext.get(id); - }else{ - + } else { } } return undefined; }, - async (id: string): Promise=>{ + async (id: string): Promise => { //await new Promise(_=>{setTimeout(_,Math.random()*10)}) const messgage = this.messages.get(id); - try{ - if(messgage){ + try { + if (messgage) { return messgage.buildhtml(); - }else{ + } else { console.error(id + " not found"); } - }catch(e){ + } catch (e) { console.error(e); } return document.createElement("div"); }, - async (id: string)=>{ + async (id: string) => { const message = this.messages.get(id); - try{ - if(message){ + try { + if (message) { message.deleteDiv(); return true; } - }catch(e){ + } catch (e) { console.error(e); - }finally{ + } finally { } return false; }, - this.readbottom.bind(this) + this.readbottom.bind(this), ); } - constructor( - json: channeljson | -1, - owner: Guild, - id: string = json === -1 ? "" : json.id - ){ + constructor(json: channeljson | -1, owner: Guild, id: string = json === -1 ? "" : json.id) { super(id); - if(json === -1){ + if (json === -1) { return; } this.editing; @@ -269,7 +314,7 @@ class Channel extends SnowFlake{ this.owner = owner; this.headers = this.owner.headers; this.name = json.name; - if(json.parent_id){ + if (json.parent_id) { this.parent_id = json.parent_id; } this.parent = undefined; @@ -277,20 +322,17 @@ class Channel extends SnowFlake{ this.guild_id = json.guild_id; this.permission_overwrites = new Map(); this.permission_overwritesar = []; - for(const thing of json.permission_overwrites){ - if(thing.id === "1182819038095799904" ||thing.id === "1182820803700625444"){ + for (const thing of json.permission_overwrites) { + if (thing.id === "1182819038095799904" || thing.id === "1182820803700625444") { continue; } - if(!this.permission_overwrites.has(thing.id)){ + if (!this.permission_overwrites.has(thing.id)) { //either a bug in the server requires this, or the API is cursed - this.permission_overwrites.set( - thing.id, - new Permissions(thing.allow, thing.deny) - ); + this.permission_overwrites.set(thing.id, new Permissions(thing.allow, thing.deny)); const permission = this.permission_overwrites.get(thing.id); - if(permission){ + if (permission) { const role = this.guild.roleids.get(thing.id); - if(role){ + if (role) { this.permission_overwritesar.push([role, permission]); } } @@ -301,126 +343,128 @@ class Channel extends SnowFlake{ this.nsfw = json.nsfw; this.position = json.position; this.lastreadmessageid = undefined; - if(json.last_message_id){ + if (json.last_message_id) { this.lastmessageid = json.last_message_id; - }else{ + } else { this.lastmessageid = undefined; } this.setUpInfiniteScroller(); this.perminfo ??= {}; - if(this.type===2&&this.localuser.voiceFactory){ - this.voice=this.localuser.voiceFactory.makeVoice(this.guild.id,this.id,{bitrate:this.bitrate}); + if (this.type === 2 && this.localuser.voiceFactory) { + this.voice = this.localuser.voiceFactory.makeVoice(this.guild.id, this.id, { + bitrate: this.bitrate, + }); this.setUpVoice(); } } - get perminfo(){ + get perminfo() { return this.guild.perminfo.channels[this.id]; } - set perminfo(e){ + set perminfo(e) { this.guild.perminfo.channels[this.id] = e; } - isAdmin(){ + isAdmin() { return this.guild.isAdmin(); } - get guild(){ + get guild() { return this.owner; } - get localuser(){ + get localuser() { return this.guild.localuser; } - get info(){ + get info() { return this.owner.info; } - readStateInfo(json: readyjson["d"]["read_state"]["entries"][0]){ + readStateInfo(json: readyjson["d"]["read_state"]["entries"][0]) { this.lastreadmessageid = json.last_message_id; this.mentions = json.mention_count; this.mentions ??= 0; this.lastpin = json.last_pin_timestamp; } - get hasunreads(): boolean{ - if(!this.hasPermission("VIEW_CHANNEL")){ + get hasunreads(): boolean { + if (!this.hasPermission("VIEW_CHANNEL")) { return false; } - return( + return ( Boolean(this.lastmessageid) && (!this.lastreadmessageid || - SnowFlake.stringToUnixTime(this.lastmessageid as string) > - SnowFlake.stringToUnixTime(this.lastreadmessageid)) && + SnowFlake.stringToUnixTime(this.lastmessageid as string) > + SnowFlake.stringToUnixTime(this.lastreadmessageid)) && this.type !== 4 ); } - hasPermission(name: string, member = this.guild.member): boolean{ - if(member.isAdmin()){ + hasPermission(name: string, member = this.guild.member): boolean { + if (member.isAdmin()) { return true; } - const roles=new Set(member.roles); - const everyone=this.guild.roles[this.guild.roles.length-1]; - if(!member.user.bot||true){ - roles.add(everyone) + const roles = new Set(member.roles); + const everyone = this.guild.roles[this.guild.roles.length - 1]; + if (!member.user.bot || true) { + roles.add(everyone); } - for(const thing of roles){ + for (const thing of roles) { const premission = this.permission_overwrites.get(thing.id); - if(premission){ + if (premission) { const perm = premission.getPermission(name); - if(perm){ + if (perm) { return perm === 1; } } - if(thing.permissions.getPermission(name)){ + if (thing.permissions.getPermission(name)) { return true; } } return false; } - get canMessage(): boolean{ - if(this.permission_overwritesar.length === 0 &&this.hasPermission("MANAGE_CHANNELS")){ - const role = this.guild.roles.find(_=>_.name === "@everyone"); - if(role){ + get canMessage(): boolean { + if (this.permission_overwritesar.length === 0 && this.hasPermission("MANAGE_CHANNELS")) { + const role = this.guild.roles.find((_) => _.name === "@everyone"); + if (role) { this.addRoleToPerms(role); } } return this.hasPermission("SEND_MESSAGES"); } - sortchildren(){ - this.children.sort((a, b)=>{ + sortchildren() { + this.children.sort((a, b) => { return a.position - b.position; }); } - resolveparent(_guild: Guild){ + resolveparent(_guild: Guild) { const parentid = this.parent_id; - if(!parentid)return false; + if (!parentid) return false; this.parent = this.localuser.channelids.get(parentid); this.parent ??= undefined; - if(this.parent !== undefined){ + if (this.parent !== undefined) { this.parent.children.push(this); } return this.parent !== undefined; } - calculateReorder(){ + calculateReorder() { let position = -1; const build: { id: string; position: number | undefined; parent_id: string | undefined; }[] = []; - for(const thing of this.children){ + for (const thing of this.children) { const thisthing: { id: string; position: number | undefined; parent_id: string | undefined; - } = { id: thing.id, position: undefined, parent_id: undefined }; + } = {id: thing.id, position: undefined, parent_id: undefined}; - if(thing.position < position){ + if (thing.position < position) { thing.position = thisthing.position = position + 1; } position = thing.position; - if(thing.move_id && thing.move_id !== thing.parent_id){ + if (thing.move_id && thing.move_id !== thing.parent_id) { thing.parent_id = thing.move_id; thisthing.parent_id = thing.parent?.id; thing.move_id = undefined; //console.log(this.guild.channelids[thisthing.parent_id.id]); } - if(thisthing.position || thisthing.parent_id){ + if (thisthing.position || thisthing.parent_id) { build.push(thisthing); } } @@ -428,35 +472,35 @@ class Channel extends SnowFlake{ } static dragged: [Channel, HTMLDivElement] | [] = []; html: WeakRef | undefined; - get visable(){ + get visable() { return this.hasPermission("VIEW_CHANNEL"); } - voiceUsers=new WeakRef(document.createElement("div")); - createguildHTML(admin = false): HTMLDivElement{ + voiceUsers = new WeakRef(document.createElement("div")); + createguildHTML(admin = false): HTMLDivElement { const div = document.createElement("div"); this.html = new WeakRef(div); - if(!this.visable){ + if (!this.visable) { let quit = true; - for(const thing of this.children){ - if(thing.visable){ + for (const thing of this.children) { + if (thing.visable) { quit = false; } } - if(quit){ + if (quit) { return div; } } // @ts-ignore I dont wanna deal with this div.all = this; div.draggable = admin; - div.addEventListener("dragstart", e=>{ + div.addEventListener("dragstart", (e) => { Channel.dragged = [this, div]; e.stopImmediatePropagation(); }); - div.addEventListener("dragend", ()=>{ + div.addEventListener("dragend", () => { Channel.dragged = []; }); - if(this.type === 4){ + if (this.type === 4) { this.sortchildren(); const caps = document.createElement("div"); @@ -471,56 +515,56 @@ class Channel extends SnowFlake{ decdiv.appendChild(myhtml); caps.appendChild(decdiv); const childrendiv = document.createElement("div"); - if(admin){ + if (admin) { const addchannel = document.createElement("span"); - addchannel.classList.add("addchannel","svgicon","svg-plus"); + addchannel.classList.add("addchannel", "svgicon", "svg-plus"); caps.appendChild(addchannel); - addchannel.onclick = _=>{ + addchannel.onclick = (_) => { this.guild.createchannels(this.createChannel.bind(this)); }; this.coatDropDiv(decdiv, childrendiv); } div.appendChild(caps); - caps.classList.add("flexltr","capsflex"); - decdiv.classList.add("flexltr","channeleffects"); + caps.classList.add("flexltr", "capsflex"); + decdiv.classList.add("flexltr", "channeleffects"); decdiv.classList.add("channel"); - Channel.contextmenu.bindContextmenu(decdiv, this,undefined); + Channel.contextmenu.bindContextmenu(decdiv, this, undefined); // @ts-ignore I dont wanna deal with this decdiv.all = this; - for(const channel of this.children){ + for (const channel of this.children) { childrendiv.appendChild(channel.createguildHTML(admin)); } childrendiv.classList.add("channels"); - setTimeout((_: any)=>{ - if(!this.perminfo.collapsed){ + setTimeout((_: any) => { + if (!this.perminfo.collapsed) { childrendiv.style.height = childrendiv.scrollHeight + "px"; } }, 100); div.appendChild(childrendiv); - if(this.perminfo.collapsed){ + if (this.perminfo.collapsed) { decoration.classList.add("hiddencat"); childrendiv.style.height = "0px"; } - decdiv.onclick = ()=>{ - if(childrendiv.style.height !== "0px"){ + decdiv.onclick = () => { + if (childrendiv.style.height !== "0px") { decoration.classList.add("hiddencat"); this.perminfo.collapsed = true; this.localuser.userinfo.updateLocal(); childrendiv.style.height = "0px"; - }else{ + } else { decoration.classList.remove("hiddencat"); this.perminfo.collapsed = false; this.localuser.userinfo.updateLocal(); childrendiv.style.height = childrendiv.scrollHeight + "px"; } }; - }else{ + } else { div.classList.add("channel"); this.unreads(); - Channel.contextmenu.bindContextmenu(div, this,undefined); - if(admin){ + Channel.contextmenu.bindContextmenu(div, this, undefined); + if (admin) { this.coatDropDiv(div); } // @ts-ignore I dont wanna deal with this @@ -531,171 +575,174 @@ class Channel extends SnowFlake{ const myhtml = document.createElement("span"); myhtml.classList.add("ellipsis"); myhtml.textContent = this.name; - if(this.type === 0){ + if (this.type === 0) { const decoration = document.createElement("span"); button.appendChild(decoration); - decoration.classList.add("space", "svgicon", this.nsfw?"svg-channelnsfw":"svg-channel"); - }else if(this.type === 2){ + decoration.classList.add("space", "svgicon", this.nsfw ? "svg-channelnsfw" : "svg-channel"); + } else if (this.type === 2) { // const decoration = document.createElement("span"); button.appendChild(decoration); - decoration.classList.add("space", "svgicon", this.nsfw?"svg-voicensfw":"svg-voice"); - }else if(this.type === 5){ + decoration.classList.add("space", "svgicon", this.nsfw ? "svg-voicensfw" : "svg-voice"); + } else if (this.type === 5) { // const decoration = document.createElement("span"); button.appendChild(decoration); - decoration.classList.add("space", "svgicon", this.nsfw?"svg-announcensfw":"svg-announce"); - }else{ + decoration.classList.add( + "space", + "svgicon", + this.nsfw ? "svg-announcensfw" : "svg-announce", + ); + } else { console.log(this.type); } button.appendChild(myhtml); - button.onclick = _=>{ + button.onclick = (_) => { this.getHTML(); const toggle = document.getElementById("maintoggle") as HTMLInputElement; toggle.checked = true; }; - if(this.type===2){ - const voiceUsers=document.createElement("div"); + if (this.type === 2) { + const voiceUsers = document.createElement("div"); div.append(voiceUsers); - this.voiceUsers=new WeakRef(voiceUsers); + this.voiceUsers = new WeakRef(voiceUsers); this.updateVoiceUsers(); } } return div; } - async moveForDrag(x:number){ - const mainarea=document.getElementById("mainarea"); - if(!mainarea) return; - if(x===-1){ + async moveForDrag(x: number) { + const mainarea = document.getElementById("mainarea"); + if (!mainarea) return; + if (x === -1) { mainarea.style.removeProperty("left"); mainarea.style.removeProperty("transition"); return; } - mainarea.style.left=x+"px"; - mainarea.style.transition="left 0s" + mainarea.style.left = x + "px"; + mainarea.style.transition = "left 0s"; } - async setUpVoice(){ - if(!this.voice) return; - this.voice.onMemberChange=async (memb,joined)=>{ - console.log(memb,joined); - if(typeof memb!=="string"){ - await Member.new(memb,this.guild); + async setUpVoice() { + if (!this.voice) return; + this.voice.onMemberChange = async (memb, joined) => { + console.log(memb, joined); + if (typeof memb !== "string") { + await Member.new(memb, this.guild); } this.updateVoiceUsers(); - if(this.voice===this.localuser.currentVoice){ + if (this.voice === this.localuser.currentVoice) { AVoice.noises("join"); } - } + }; } - async updateVoiceUsers(){ - const voiceUsers=this.voiceUsers.deref(); - if(!voiceUsers||!this.voice) return; - console.warn(this.voice.userids) + async updateVoiceUsers() { + const voiceUsers = this.voiceUsers.deref(); + if (!voiceUsers || !this.voice) return; + console.warn(this.voice.userids); - const html=(await Promise.all(this.voice.userids.entries().toArray().map(async _=>{ - const user=await User.resolve(_[0],this.localuser); - console.log(user); - const member=await Member.resolveMember(user,this.guild); - const array=[member,_[1]] as [Member, typeof _[1]]; - return array; - }))).flatMap(([member,_obj])=>{ - if(!member){ + const html = ( + await Promise.all( + this.voice.userids + .entries() + .toArray() + .map(async (_) => { + const user = await User.resolve(_[0], this.localuser); + console.log(user); + const member = await Member.resolveMember(user, this.guild); + const array = [member, _[1]] as [Member, (typeof _)[1]]; + return array; + }), + ) + ).flatMap(([member, _obj]) => { + if (!member) { console.warn("This is weird, member doesn't exist :P"); return []; } - const div=document.createElement("div"); + const div = document.createElement("div"); div.classList.add("voiceuser"); - const span=document.createElement("span"); - span.textContent=member.name; + const span = document.createElement("span"); + span.textContent = member.name; div.append(span); return div; }); - voiceUsers.innerHTML=""; + voiceUsers.innerHTML = ""; voiceUsers.append(...html); } - get myhtml(){ - if(this.html){ + get myhtml() { + if (this.html) { return this.html.deref(); - }else{ + } else { return; } } - readbottom(){ - this.mentions=0; - if(!this.hasunreads){ + readbottom() { + this.mentions = 0; + if (!this.hasunreads) { this.guild.unreads(); return; } - fetch( - this.info.api +"/channels/" + this.id + "/messages/" + this.lastmessageid + "/ack", - { - method: "POST", - headers: this.headers, - body: JSON.stringify({}), - } - ); + fetch(this.info.api + "/channels/" + this.id + "/messages/" + this.lastmessageid + "/ack", { + method: "POST", + headers: this.headers, + body: JSON.stringify({}), + }); this.lastreadmessageid = this.lastmessageid; this.guild.unreads(); this.unreads(); } - coatDropDiv(div: HTMLDivElement, container: HTMLElement | boolean = false){ - div.addEventListener("dragenter", event=>{ + coatDropDiv(div: HTMLDivElement, container: HTMLElement | boolean = false) { + div.addEventListener("dragenter", (event) => { console.log("enter"); event.preventDefault(); }); - div.addEventListener("dragover", event=>{ + div.addEventListener("dragover", (event) => { event.preventDefault(); }); - div.addEventListener("drop", event=>{ + div.addEventListener("drop", (event) => { const that = Channel.dragged[0]; - if(!that)return; + if (!that) return; event.preventDefault(); - if(container){ + if (container) { that.move_id = this.id; - if(that.parent){ + if (that.parent) { that.parent.children.splice(that.parent.children.indexOf(that), 1); } that.parent = this; - (container as HTMLElement).prepend( - Channel.dragged[1] as HTMLDivElement - ); + (container as HTMLElement).prepend(Channel.dragged[1] as HTMLDivElement); this.children.unshift(that); - }else{ + } else { console.log(this, Channel.dragged); that.move_id = this.parent_id; - if(that.parent){ + if (that.parent) { that.parent.children.splice(that.parent.children.indexOf(that), 1); - }else{ - this.guild.headchannels.splice( - this.guild.headchannels.indexOf(that), - 1 - ); + } else { + this.guild.headchannels.splice(this.guild.headchannels.indexOf(that), 1); } that.parent = this.parent; - if(that.parent){ + if (that.parent) { const build: Channel[] = []; - for(let i = 0; i < that.parent.children.length; i++){ + for (let i = 0; i < that.parent.children.length; i++) { build.push(that.parent.children[i]); - if(that.parent.children[i] === this){ + if (that.parent.children[i] === this) { build.push(that); } } that.parent.children = build; - }else{ + } else { const build: Channel[] = []; - for(let i = 0; i < this.guild.headchannels.length; i++){ + for (let i = 0; i < this.guild.headchannels.length; i++) { build.push(this.guild.headchannels[i]); - if(this.guild.headchannels[i] === this){ + if (this.guild.headchannels[i] === this) { build.push(that); } } this.guild.headchannels = build; } - if(Channel.dragged[1]){ + if (Channel.dragged[1]) { div.after(Channel.dragged[1]); } } @@ -704,7 +751,7 @@ class Channel extends SnowFlake{ return div; } - createChannel(name: string, type: number){ + createChannel(name: string, type: number) { fetch(this.info.api + "/guilds/" + this.guild.id + "/channels", { method: "POST", headers: this.headers, @@ -716,34 +763,34 @@ class Channel extends SnowFlake{ }), }); } - deleteChannel(){ + deleteChannel() { fetch(this.info.api + "/channels/" + this.id, { method: "DELETE", headers: this.headers, }); } - setReplying(message: Message){ - if(this.replyingto?.div){ + setReplying(message: Message) { + if (this.replyingto?.div) { this.replyingto.div.classList.remove("replying"); } this.replyingto = message; const typebox = document.getElementById("typebox") as HTMLElement; typebox.focus(); - if(!this.replyingto?.div)return; + if (!this.replyingto?.div) return; console.log(message); this.replyingto.div.classList.add("replying"); this.makereplybox(); } - makereplybox(){ + makereplybox() { const replybox = document.getElementById("replybox") as HTMLElement; const typebox = document.getElementById("typebox") as HTMLElement; - if(this.replyingto){ + if (this.replyingto) { replybox.innerHTML = ""; const span = document.createElement("span"); span.textContent = I18n.getTranslation("replyingTo", this.replyingto.author.username); const X = document.createElement("button"); - X.onclick = _=>{ - if(this.replyingto?.div){ + X.onclick = (_) => { + if (this.replyingto?.div) { this.replyingto.div.classList.remove("replying"); } replybox.classList.add("hideReplyBox"); @@ -752,125 +799,121 @@ class Channel extends SnowFlake{ typebox.classList.remove("typeboxreplying"); }; replybox.classList.remove("hideReplyBox"); - X.classList.add("cancelReply","svgicon","svg-x"); + X.classList.add("cancelReply", "svgicon", "svg-x"); replybox.append(span); replybox.append(X); typebox.classList.add("typeboxreplying"); - }else{ + } else { replybox.classList.add("hideReplyBox"); typebox.classList.remove("typeboxreplying"); } } - async getmessage(id: string): Promise{ + async getmessage(id: string): Promise { const message = this.messages.get(id); - if(message){ + if (message) { return message; - }else{ + } else { const gety = await fetch( - this.info.api + "/channels/" +this.id +"/messages?limit=1&around=" +id, - { headers: this.headers } + this.info.api + "/channels/" + this.id + "/messages?limit=1&around=" + id, + {headers: this.headers}, ); const json = await gety.json(); - if(json.length===0){ + if (json.length === 0) { return undefined; } return new Message(json[0], this); } } - async focus(id:string){ - console.time() + async focus(id: string) { + console.time(); console.log(await this.getmessage(id)); await this.getHTML(); - console.timeEnd() + console.timeEnd(); console.warn(id); this.infinite.focus(id); } - editLast(){ - let message:Message|undefined=this.lastmessage; - while(message&&message.author!==this.localuser.user){ - message=this.messages.get(this.idToPrev.get(message.id) as string); + editLast() { + let message: Message | undefined = this.lastmessage; + while (message && message.author !== this.localuser.user) { + message = this.messages.get(this.idToPrev.get(message.id) as string); } - if(message){ + if (message) { message.setEdit(); } } 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"; + 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("channelw") as HTMLDivElement; - const messageContainers = Array.from( - messages.getElementsByClassName("messagecontainer") - ); - for(const thing of messageContainers){ + const messageContainers = Array.from(messages.getElementsByClassName("messagecontainer")); + for (const thing of messageContainers) { thing.remove(); } const elements = Array.from(messages.getElementsByClassName("scroller")); - for(const elm of elements){ + for (const elm of elements) { elm.remove(); console.warn("rouge element detected and removed"); } - const div=document.getElementById("sideDiv") as HTMLDivElement; - div.innerHTML=""; - const float=new Float(""); - const options=float.options; + const div = document.getElementById("sideDiv") as HTMLDivElement; + div.innerHTML = ""; + const float = new Float(""); + const options = float.options; //@ts-ignore weird hack, ik, but the user here does have that information //TODO make an extention of the user class with these aditional properties //TODO make a popup for `nsfw_allowed==null` to input age - if(this.localuser.user.nsfw_allowed){ + if (this.localuser.user.nsfw_allowed) { options.addTitle("This is a NSFW channel, do you wish to proceed?"); - const buttons=options.addOptions("",{ltr:true}); - buttons.addButtonInput("","Yes",()=>{ - this.perminfo.nsfwOk=true; + const buttons = options.addOptions("", {ltr: true}); + buttons.addButtonInput("", "Yes", () => { + this.perminfo.nsfwOk = true; this.localuser.userinfo.updateLocal(); this.getHTML(); }); - buttons.addButtonInput("","No",()=>{ + buttons.addButtonInput("", "No", () => { window.history.back(); - }) - }else{ + }); + } else { options.addTitle("You are not allowed in this channel."); } - const html=float.generateHTML(); - html.classList.add("messagecontainer") + const html = float.generateHTML(); + html.classList.add("messagecontainer"); messages.append(html); - } - async getHTML(addstate=true){ - - if(addstate){ - history.pushState([this.guild_id,this.id], "", "/channels/" + this.guild_id + "/" + this.id); + async getHTML(addstate = true) { + if (addstate) { + history.pushState([this.guild_id, this.id], "", "/channels/" + this.guild_id + "/" + this.id); } this.localuser.pageTitle("#" + this.name); const channelTopic = document.getElementById("channelTopic") as HTMLSpanElement; - if(this.topic){ - channelTopic.innerHTML =""; - channelTopic.append(new MarkDown( - this.topic, - this - ).makeHTML()); + if (this.topic) { + channelTopic.innerHTML = ""; + channelTopic.append(new MarkDown(this.topic, this).makeHTML()); channelTopic.removeAttribute("hidden"); - }else channelTopic.setAttribute("hidden", ""); - if(this.guild !== this.localuser.lookingguild){ + } else channelTopic.setAttribute("hidden", ""); + if (this.guild !== this.localuser.lookingguild) { this.guild.loadGuild(); } - if(this.localuser.channelfocus && this.localuser.channelfocus.myhtml){ + if (this.localuser.channelfocus && this.localuser.channelfocus.myhtml) { this.localuser.channelfocus.myhtml.classList.remove("viewChannel"); } - if(this.myhtml){ + if (this.myhtml) { this.myhtml.classList.add("viewChannel"); } const id = ++Channel.genid; - if(this.localuser.channelfocus){ + if (this.localuser.channelfocus) { this.localuser.channelfocus.infinite.delete(); } this.guild.prevchannel = this; this.guild.perminfo.prevchannel = this.id; this.localuser.userinfo.updateLocal(); this.localuser.channelfocus = this; - //@ts-ignore another hack - if(this.nsfw&&(!this.perminfo.nsfwOk||!this.localuser.user.nsfw_allowed)){ + + if ( + this.nsfw && //@ts-ignore another hack + (!this.perminfo.nsfwOk || !this.localuser.user.nsfw_allowed) + ) { this.nsfwPannel(); return; } @@ -882,29 +925,30 @@ class Channel extends SnowFlake{ loading.classList.add("loading"); this.rendertyping(); this.localuser.getSidePannel(); - if(this.voice&&localStorage.getItem("Voice enabled")){ + if (this.voice && localStorage.getItem("Voice enabled")) { this.localuser.joinVoice(this); } - (document.getElementById("typebox") as HTMLDivElement).contentEditable =""+this.canMessage; - (document.getElementById("upload") as HTMLElement).style.visibility=this.canMessage?"visible":"hidden"; - (document.getElementById("typediv") as HTMLElement).style.visibility="visible"; + (document.getElementById("typebox") as HTMLDivElement).contentEditable = "" + this.canMessage; + (document.getElementById("upload") as HTMLElement).style.visibility = this.canMessage + ? "visible" + : "hidden"; + (document.getElementById("typediv") as HTMLElement).style.visibility = "visible"; (document.getElementById("typebox") as HTMLDivElement).focus(); await this.putmessages(); await prom; - if(id !== Channel.genid){ + if (id !== Channel.genid) { return; } this.makereplybox(); await this.buildmessages(); //loading.classList.remove("loading"); - } typingmap: Map = new Map(); - async typingStart(typing: startTypingjson): Promise{ + async typingStart(typing: startTypingjson): Promise { const memb = await Member.new(typing.d.member!, this.guild); - if(!memb)return; - if(memb.id === this.localuser.user.id){ + if (!memb) return; + if (memb.id === this.localuser.user.id) { console.log("you is typing"); return; } @@ -913,58 +957,56 @@ class Channel extends SnowFlake{ setTimeout(this.rendertyping.bind(this), 10000); this.rendertyping(); } - similar(str:string){ - if(this.type===4) return -1; - const strl=Math.max(str.length,1) - if(this.name.includes(str)){ - return strl/this.name.length; - }else if(this.name.toLowerCase().includes(str.toLowerCase())){ - return strl/this.name.length/1.2; + similar(str: string) { + if (this.type === 4) return -1; + const strl = Math.max(str.length, 1); + if (this.name.includes(str)) { + return strl / this.name.length; + } else if (this.name.toLowerCase().includes(str.toLowerCase())) { + return strl / this.name.length / 1.2; } return 0; } - rendertyping(): void{ + rendertyping(): void { const typingtext = document.getElementById("typing") as HTMLDivElement; let build = ""; let showing = false; let i = 0; const curtime = Date.now() - 5000; - for(const thing of this.typingmap.keys()){ - if((this.typingmap.get(thing) as number) > curtime){ - if(i !== 0){ + for (const thing of this.typingmap.keys()) { + if ((this.typingmap.get(thing) as number) > curtime) { + if (i !== 0) { build += ", "; } i++; - if(thing.nick){ + if (thing.nick) { build += thing.nick; - }else{ + } else { build += thing.user.username; } showing = true; - }else{ + } else { this.typingmap.delete(thing); } } - build=I18n.getTranslation("typing",i+"",build); - if(this.localuser.channelfocus === this){ - if(showing){ + build = I18n.getTranslation("typing", i + "", build); + if (this.localuser.channelfocus === this) { + if (showing) { typingtext.classList.remove("hidden"); - const typingtext2 = document.getElementById( - "typingtext" - ) as HTMLDivElement; + const typingtext2 = document.getElementById("typingtext") as HTMLDivElement; typingtext2.textContent = build; - }else{ + } else { typingtext.classList.add("hidden"); } } } - static regenLoadingMessages(){ + static regenLoadingMessages() { const loading = document.getElementById("loadingdiv") as HTMLDivElement; loading.innerHTML = ""; - for(let i = 0; i < 15; i++){ + for (let i = 0; i < 15; i++) { const div = document.createElement("div"); div.classList.add("loadingmessage"); - if(Math.random() < 0.5){ + if (Math.random() < 0.5) { const pfp = document.createElement("div"); pfp.classList.add("loadingpfp"); const username = document.createElement("div"); @@ -981,36 +1023,50 @@ class Channel extends SnowFlake{ } } lastmessage: Message | undefined; - setnotifcation(){ - const defualt=I18n.getTranslation("guild."+["all", "onlyMentions", "none","default"][this.guild.message_notifications]) - const options=["all", "onlyMentions", "none","default"].map(e=>I18n.getTranslation("guild."+e,defualt)); - const notiselect=new Dialog(""); - const form=notiselect.options.addForm("",(_,sent:any)=>{ - notiselect.hide(); - console.log(sent); - this.message_notifications = sent.channel_overrides[this.id].message_notifications; - },{ - fetchURL:`${this.info.api}/users/@me/guilds/${this.guild.id}/settings/`, - method:"PATCH", - headers:this.headers - }); - form.addSelect(I18n.getTranslation("guild.selectnoti"),"message_notifications",options,{ - radio:true, - defaultIndex:this.message_notifications - },[0,1,2,3]); + setnotifcation() { + const defualt = I18n.getTranslation( + "guild." + ["all", "onlyMentions", "none", "default"][this.guild.message_notifications], + ); + const options = ["all", "onlyMentions", "none", "default"].map((e) => + I18n.getTranslation("guild." + e, defualt), + ); + const notiselect = new Dialog(""); + const form = notiselect.options.addForm( + "", + (_, sent: any) => { + notiselect.hide(); + console.log(sent); + this.message_notifications = sent.channel_overrides[this.id].message_notifications; + }, + { + fetchURL: `${this.info.api}/users/@me/guilds/${this.guild.id}/settings/`, + method: "PATCH", + headers: this.headers, + }, + ); + form.addSelect( + I18n.getTranslation("guild.selectnoti"), + "message_notifications", + options, + { + radio: true, + defaultIndex: this.message_notifications, + }, + [0, 1, 2, 3], + ); - form.addPreprocessor((e:any)=>{ - const message_notifications=e.message_notifications; + form.addPreprocessor((e: any) => { + const message_notifications = e.message_notifications; delete e.message_notifications; - e.channel_overrides={ - [this.id]:{ + e.channel_overrides = { + [this.id]: { message_notifications, - muted:this.muted, - mute_config:this.mute_config, - channel_id:this.id - } - } - }) + muted: this.muted, + mute_config: this.mute_config, + channel_id: this.id, + }, + }; + }); /* let noti = this.message_notifications; const defualt=I18n.getTranslation("guild."+["all", "onlyMentions", "none","default"][this.guild.message_notifications]) @@ -1056,74 +1112,69 @@ class Channel extends SnowFlake{ */ notiselect.show(); } - async putmessages(){ + async putmessages() { //TODO swap out with the WS op code - if(this.allthewayup){ + if (this.allthewayup) { return; } - if(this.lastreadmessageid && this.messages.has(this.lastreadmessageid)){ + if (this.lastreadmessageid && this.messages.has(this.lastreadmessageid)) { return; } - const j = await fetch( - this.info.api + "/channels/" + this.id + "/messages?limit=100", - { - headers: this.headers, - } - ); + const j = await fetch(this.info.api + "/channels/" + this.id + "/messages?limit=100", { + headers: this.headers, + }); const response = await j.json(); - if(response.length !== 100){ + if (response.length !== 100) { this.allthewayup = true; } let prev: Message | undefined; - for(const thing of response){ + for (const thing of response) { const message = new Message(thing, this); - if(prev){ + if (prev) { this.idToNext.set(message.id, prev.id); this.idToPrev.set(prev.id, message.id); - }else{ + } else { this.lastmessage = message; this.lastmessageid = message.id; } prev = message; } } - delChannel(json: channeljson){ + delChannel(json: channeljson) { const build: Channel[] = []; - for(const thing of this.children){ - if(thing.id !== json.id){ + for (const thing of this.children) { + if (thing.id !== json.id) { build.push(thing); } } this.children = build; } - async grabAfter(id: string){ - if(id === this.lastmessage?.id){ + async grabAfter(id: string) { + if (id === this.lastmessage?.id) { return; } - await fetch( - this.info.api + "/channels/" +this.id +"/messages?limit=100&after=" +id,{ - headers: this.headers, - } - ) - .then(j=>{ + await fetch(this.info.api + "/channels/" + this.id + "/messages?limit=100&after=" + id, { + headers: this.headers, + }) + .then((j) => { return j.json(); }) - .then(response=>{ + .then((response) => { let previd: string = id; - for(const i in response){ + for (const i in response) { let messager: Message; let willbreak = false; - if(this.messages.has(response[i].id)){ + if (this.messages.has(response[i].id)) { messager = this.messages.get(response[i].id) as Message; willbreak = true; - }else{ + } else { messager = new Message(response[i], this); } this.idToPrev.set(messager.id, previd); this.idToNext.set(previd, messager.id); previd = messager.id; - if(willbreak){ + if (willbreak) { break; } } @@ -1131,36 +1182,33 @@ class Channel extends SnowFlake{ }); } topid!: string; - async grabBefore(id: string){ - if(this.topid && id === this.topid){ + async grabBefore(id: string) { + if (this.topid && id === this.topid) { return; } - await fetch( - this.info.api + "/channels/" + this.id +"/messages?before=" + id + "&limit=100", - { - headers: this.headers, - } - ) - .then(j=>{ + await fetch(this.info.api + "/channels/" + this.id + "/messages?before=" + id + "&limit=100", { + headers: this.headers, + }) + .then((j) => { return j.json(); }) - .then((response: messagejson[])=>{ - if(response.length < 100){ + .then((response: messagejson[]) => { + if (response.length < 100) { this.allthewayup = true; - if(response.length === 0){ + if (response.length === 0) { this.topid = id; } } let previd = id; - for(const i in response){ + for (const i in response) { let messager: Message; let willbreak = false; - if(this.messages.has(response[i].id)){ + if (this.messages.has(response[i].id)) { console.log("flaky"); messager = this.messages.get(response[i].id) as Message; willbreak = true; - }else{ + } else { messager = new Message(response[i], this); } @@ -1168,171 +1216,161 @@ class Channel extends SnowFlake{ this.idToPrev.set(previd, messager.id); previd = messager.id; - if(Number(i) === response.length - 1 && response.length < 100){ + if (Number(i) === response.length - 1 && response.length < 100) { this.topid = previd; } - if(willbreak){ + if (willbreak) { break; } } }); } /** - * Please dont use this, its not implemented. - * @deprecated - * @todo - **/ - async grabArround(/* id: string */){ + * Please dont use this, its not implemented. + * @deprecated + * @todo + **/ + async grabArround(/* id: string */) { //currently unused and no plans to use it yet throw new Error("please don't call this, no one has implemented it :P"); } - async buildmessages(){ + async buildmessages() { this.infinitefocus = false; await this.tryfocusinfinate(); } infinitefocus = false; - async tryfocusinfinate(){ - if(this.infinitefocus)return; + async tryfocusinfinate() { + if (this.infinitefocus) return; this.infinitefocus = true; const messages = document.getElementById("channelw") as HTMLDivElement; - const messageContainers = Array.from( - messages.getElementsByClassName("messagecontainer") - ); - for(const thing of messageContainers){ + const messageContainers = Array.from(messages.getElementsByClassName("messagecontainer")); + for (const thing of messageContainers) { thing.remove(); } const loading = document.getElementById("loadingdiv") as HTMLDivElement; const removetitle = document.getElementById("removetitle"); //messages.innerHTML=""; let id: string | undefined; - if(this.lastreadmessageid && this.messages.has(this.lastreadmessageid)){ + if (this.lastreadmessageid && this.messages.has(this.lastreadmessageid)) { id = this.lastreadmessageid; - }else if(this.lastreadmessageid && (id = this.findClosest(this.lastreadmessageid))){ - - }else if(this.lastmessageid && this.messages.has(this.lastmessageid)){ + } else if (this.lastreadmessageid && (id = this.findClosest(this.lastreadmessageid))) { + } else if (this.lastmessageid && this.messages.has(this.lastmessageid)) { id = this.goBackIds(this.lastmessageid, 50); } - if(!id){ - if(!removetitle){ + if (!id) { + if (!removetitle) { const title = document.createElement("h2"); title.id = "removetitle"; title.textContent = I18n.getTranslation("noMessages"); - title.classList.add("titlespace","messagecontainer"); + title.classList.add("titlespace", "messagecontainer"); messages.append(title); } this.infinitefocus = false; loading.classList.remove("loading"); return; - }else if(removetitle){ + } else if (removetitle) { removetitle.remove(); } - if(this.localuser.channelfocus !== this){ + if (this.localuser.channelfocus !== this) { return; } const elements = Array.from(messages.getElementsByClassName("scroller")); - for(const elm of elements){ + for (const elm of elements) { elm.remove(); console.warn("rouge element detected and removed"); } messages.append(await this.infinite.getDiv(id)); this.infinite.updatestuff(); - this.infinite.watchForChange().then(async _=>{ + this.infinite.watchForChange().then(async (_) => { //await new Promise(resolve => setTimeout(resolve, 0)); this.infinite.focus(id, false); //if someone could figure out how to make this work correctly without this, that's be great :P loading.classList.remove("loading"); }); //this.infinite.focus(id.id,false); } - private goBackIds( - id: string, - back: number, - returnifnotexistant = true - ): string | undefined{ - while(back !== 0){ + private goBackIds(id: string, back: number, returnifnotexistant = true): string | undefined { + while (back !== 0) { const nextid = this.idToPrev.get(id); - if(nextid){ + if (nextid) { id = nextid; back--; - }else{ - if(returnifnotexistant){ + } else { + if (returnifnotexistant) { break; - }else{ + } else { return undefined; } } } return id; } - private findClosest(id: string | undefined){ - if(!this.lastmessageid || !id)return; + private findClosest(id: string | undefined) { + if (!this.lastmessageid || !id) return; let flake: string | undefined = this.lastmessageid; const time = SnowFlake.stringToUnixTime(id); let flaketime = SnowFlake.stringToUnixTime(flake); - while(flake && time < flaketime){ + while (flake && time < flaketime) { flake = this.idToPrev.get(flake); - if(!flake){ + if (!flake) { return; } flaketime = SnowFlake.stringToUnixTime(flake); } return flake; } - updateChannel(json: channeljson){ + updateChannel(json: channeljson) { this.type = json.type; this.name = json.name; const parent = this.localuser.channelids.get(json.parent_id); - if(parent){ + if (parent) { this.parent = parent; this.parent_id = parent.id; - }else{ + } else { this.parent = undefined; this.parent_id = undefined; } this.children = []; this.guild_id = json.guild_id; - const oldover=this.permission_overwrites; + const oldover = this.permission_overwrites; this.permission_overwrites = new Map(); - this.permission_overwritesar=[]; - for(const thing of json.permission_overwrites){ - if(thing.id === "1182819038095799904" || thing.id === "1182820803700625444"){ + this.permission_overwritesar = []; + for (const thing of json.permission_overwrites) { + if (thing.id === "1182819038095799904" || thing.id === "1182820803700625444") { continue; } - this.permission_overwrites.set( - thing.id, - new Permissions(thing.allow, thing.deny) - ); + this.permission_overwrites.set(thing.id, new Permissions(thing.allow, thing.deny)); const permisions = this.permission_overwrites.get(thing.id); - if(permisions){ + if (permisions) { const role = this.guild.roleids.get(thing.id); - if(role){ + if (role) { this.permission_overwritesar.push([role, permisions]); } } } - const nchange=[...new Set().union(oldover).difference(this.permission_overwrites)]; - const pchange=[...new Set().union(this.permission_overwrites).difference(oldover)]; - for(const thing of nchange){ - const role=this.guild.roleids.get(thing); - if(role){ - this.croleUpdate(role,new Permissions("0"),false) + const nchange = [...new Set().union(oldover).difference(this.permission_overwrites)]; + const pchange = [...new Set().union(this.permission_overwrites).difference(oldover)]; + for (const thing of nchange) { + const role = this.guild.roleids.get(thing); + if (role) { + this.croleUpdate(role, new Permissions("0"), false); } } - for(const thing of pchange){ - const role=this.guild.roleids.get(thing); - const perms=this.permission_overwrites.get(thing); - if(role&&perms){ - this.croleUpdate(role,perms,true); + for (const thing of pchange) { + const role = this.guild.roleids.get(thing); + const perms = this.permission_overwrites.get(thing); + if (role && perms) { + this.croleUpdate(role, perms, true); } } - console.log(pchange,nchange); + console.log(pchange, nchange); this.topic = json.topic; this.nsfw = json.nsfw; } - croleUpdate:(role:Role,perm:Permissions,added:boolean)=>unknown=()=>{}; - typingstart(){ - if(this.typing > Date.now()){ + croleUpdate: (role: Role, perm: Permissions, added: boolean) => unknown = () => {}; + typingstart() { + if (this.typing > Date.now()) { return; } this.typing = Date.now() + 6000; @@ -1341,23 +1379,23 @@ class Channel extends SnowFlake{ headers: this.headers, }); } - get notification(){ + get notification() { let notinumber: number | null = this.message_notifications; - if(Number(notinumber) === 3){ + if (Number(notinumber) === 3) { notinumber = null; } notinumber ??= this.guild.message_notifications; - console.warn("info:",notinumber); - switch(Number(notinumber)){ + console.warn("info:", notinumber); + switch (Number(notinumber)) { case 0: - return"all"; + return "all"; case 1: - return"mentions"; + return "mentions"; case 2: - return"none"; + return "none"; case 3: default: - return"default"; + return "default"; } } async sendMessage( @@ -1365,23 +1403,23 @@ class Channel extends SnowFlake{ { attachments = [], replyingto = null, - }: { attachments: Blob[]; embeds: embedjson; replyingto: Message | null } - ){ + }: {attachments: Blob[]; embeds: embedjson; replyingto: Message | null}, + ) { let replyjson: any; - if(replyingto){ + if (replyingto) { replyjson = { guild_id: replyingto.guild.id, channel_id: replyingto.channel.id, message_id: replyingto.id, }; } - if(attachments.length === 0){ + if (attachments.length === 0) { const body = { content, nonce: Math.floor(Math.random() * 1000000000), message_reference: undefined, }; - if(replyjson){ + if (replyjson) { body.message_reference = replyjson; } return await fetch(this.info.api + "/channels/" + this.id + "/messages", { @@ -1389,108 +1427,102 @@ class Channel extends SnowFlake{ headers: this.headers, body: JSON.stringify(body), }); - }else{ + } else { const formData = new FormData(); const body = { content, nonce: Math.floor(Math.random() * 1000000000), message_reference: undefined, }; - if(replyjson){ + if (replyjson) { body.message_reference = replyjson; } formData.append("payload_json", JSON.stringify(body)); - for(const i in attachments){ + for (const i in attachments) { formData.append("files[" + i + "]", attachments[i]); } return await fetch(this.info.api + "/channels/" + this.id + "/messages", { method: "POST", body: formData, - headers: { Authorization: this.headers.Authorization }, + headers: {Authorization: this.headers.Authorization}, }); } } - unreads(){ - if(!this.hasunreads){ - if(this.myhtml){ - this.myhtml.classList.remove("cunread","mentioned"); + unreads() { + if (!this.hasunreads) { + if (this.myhtml) { + this.myhtml.classList.remove("cunread", "mentioned"); } - }else{ - if(this.myhtml){ + } else { + if (this.myhtml) { this.myhtml.classList.add("cunread"); } - if(this.mentions!==0){ - this.myhtml?.classList.add("mentioned") + if (this.mentions !== 0) { + this.myhtml?.classList.add("mentioned"); } } } - messageCreate(messagep: messageCreateJson): void{ - if(!this.hasPermission("VIEW_CHANNEL")){ + messageCreate(messagep: messageCreateJson): void { + if (!this.hasPermission("VIEW_CHANNEL")) { return; } const messagez = new Message(messagep.d, this); this.lastmessage = messagez; - if(this.lastmessageid){ + if (this.lastmessageid) { this.idToNext.set(this.lastmessageid, messagez.id); this.idToPrev.set(messagez.id, this.lastmessageid); } - if(messagez.mentionsuser(this.localuser.user)&&messagez.author!==this.localuser.user){ + if (messagez.mentionsuser(this.localuser.user) && messagez.author !== this.localuser.user) { this.mentions++; } this.lastmessageid = messagez.id; - if(messagez.author === this.localuser.user){ + if (messagez.author === this.localuser.user) { this.lastreadmessageid = messagez.id; } this.unreads(); this.guild.unreads(); - if(this === this.localuser.channelfocus){ - if(!this.infinitefocus){ + if (this === this.localuser.channelfocus) { + if (!this.infinitefocus) { this.tryfocusinfinate(); } this.infinite.addedBottom(); } - if(messagez.author === this.localuser.user){ + if (messagez.author === this.localuser.user) { return; } - if( - this.localuser.lookingguild?.prevchannel === this && document.hasFocus() - ){ + if (this.localuser.lookingguild?.prevchannel === this && document.hasFocus()) { return; } - if(this.notification === "all"){ + if (this.notification === "all") { this.notify(messagez); - }else if( - this.notification === "mentions" && messagez.mentionsuser(this.localuser.user) - ){ + } else if (this.notification === "mentions" && messagez.mentionsuser(this.localuser.user)) { this.notify(messagez); } } - notititle(message: Message): string{ - return( - message.author.username + " > " + this.guild.properties.name + " > " + this.name - ); + notititle(message: Message): string { + return message.author.username + " > " + this.guild.properties.name + " > " + this.name; } - notify(message: Message, deep = 0){ - if(this.localuser.play){ - const voice=this.localuser.play.audios.get(this.localuser.getNotificationSound()); - if(voice){ + notify(message: Message, deep = 0) { + if (this.localuser.play) { + const voice = this.localuser.play.audios.get(this.localuser.getNotificationSound()); + if (voice) { voice.play(); } } - if(!("Notification" in window)){ - }else if(Notification.permission === "granted"){ + if (!("Notification" in window)) { + } else if (Notification.permission === "granted") { let noticontent: string | undefined | null = message.content.textContent; - if(message.embeds[0]){ + if (message.embeds[0]) { noticontent ||= message.embeds[0]?.json.title; noticontent ||= message.content.textContent; } noticontent ||= I18n.getTranslation("blankMessage"); let imgurl: null | string = null; const images = message.getimages(); - if(images.length){ + if (images.length) { const image = images[0]; - if(image.proxy_url){ + if (image.proxy_url) { imgurl ||= image.proxy_url; } imgurl ||= image.url; @@ -1500,61 +1532,53 @@ class Channel extends SnowFlake{ icon: message.author.getpfpsrc(this.guild), image: imgurl, }); - notification.addEventListener("click", _=>{ + notification.addEventListener("click", (_) => { window.focus(); this.getHTML(); }); - }else if(Notification.permission !== "denied"){ - Notification.requestPermission().then(()=>{ - if(deep === 3){ + } else if (Notification.permission !== "denied") { + Notification.requestPermission().then(() => { + if (deep === 3) { return; } this.notify(message, deep + 1); }); } } - async addRoleToPerms(role: Role){ - await fetch( - this.info.api + "/channels/" + this.id + "/permissions/" + role.id, - { - method: "PUT", - headers: this.headers, - body: JSON.stringify({ - allow: "0", - deny: "0", - id: role.id, - type: 0, - }), - } - ); + async addRoleToPerms(role: Role) { + await fetch(this.info.api + "/channels/" + this.id + "/permissions/" + role.id, { + method: "PUT", + headers: this.headers, + body: JSON.stringify({ + allow: "0", + deny: "0", + id: role.id, + type: 0, + }), + }); const perm = new Permissions("0", "0"); this.permission_overwrites.set(role.id, perm); this.permission_overwritesar.push([role, perm]); } - async updateRolePermissions(id: string, perms: Permissions){ + async updateRolePermissions(id: string, perms: Permissions) { const permission = this.permission_overwrites.get(id); - if(permission){ + if (permission) { permission.allow = perms.allow; permission.deny = perms.deny; - }else{ + } else { //this.permission_overwrites.set(id,perms); } - await fetch( - this.info.api + "/channels/" + this.id + "/permissions/" + id, - { - method: "PUT", - headers: this.headers, - body: JSON.stringify({ - allow: perms.allow.toString(), - deny: perms.deny.toString(), - id, - type: 0, - }), - } - ); + await fetch(this.info.api + "/channels/" + this.id + "/permissions/" + id, { + method: "PUT", + headers: this.headers, + body: JSON.stringify({ + allow: perms.allow.toString(), + deny: perms.deny.toString(), + id, + type: 0, + }), + }); } } Channel.setupcontextmenu(); -export{ Channel }; - - +export {Channel}; diff --git a/src/webpage/contextmenu.ts b/src/webpage/contextmenu.ts index 794fd88..fe73808 100644 --- a/src/webpage/contextmenu.ts +++ b/src/webpage/contextmenu.ts @@ -1,82 +1,82 @@ -import{ iOS }from"./utils/utils.js"; -class Contextmenu{ +import {iOS} from "./utils/utils.js"; +class Contextmenu { static currentmenu: HTMLElement | ""; name: string; buttons: [ - string|(()=>string), + string | (() => string), (this: x, arg: y, e: MouseEvent) => void, string | null, (this: x, arg: y) => boolean, (this: x, arg: y) => boolean, - string + string, ][]; div!: HTMLDivElement; - static setup(){ + static setup() { Contextmenu.currentmenu = ""; - document.addEventListener("click", event=>{ - if(Contextmenu.currentmenu === ""){ + document.addEventListener("click", (event) => { + if (Contextmenu.currentmenu === "") { return; } - if(!Contextmenu.currentmenu.contains(event.target as Node)){ + if (!Contextmenu.currentmenu.contains(event.target as Node)) { Contextmenu.currentmenu.remove(); Contextmenu.currentmenu = ""; } }); } - constructor(name: string){ + constructor(name: string) { this.name = name; this.buttons = []; } addbutton( - text: string|(()=>string), + text: string | (() => string), onclick: (this: x, arg: y, e: MouseEvent) => void, img: null | string = null, - shown: (this: x, arg: y) => boolean = _=>true, - enabled: (this: x, arg: y) => boolean = _=>true - ){ + shown: (this: x, arg: y) => boolean = (_) => true, + enabled: (this: x, arg: y) => boolean = (_) => true, + ) { this.buttons.push([text, onclick, img, shown, enabled, "button"]); - return{}; + return {}; } addsubmenu( - text: string|(()=>string), + text: string | (() => string), onclick: (this: x, arg: y, e: MouseEvent) => void, img = null, - shown: (this: x, arg: y) => boolean = _=>true, - enabled: (this: x, arg: y) => boolean = _=>true - ){ + shown: (this: x, arg: y) => boolean = (_) => true, + enabled: (this: x, arg: y) => boolean = (_) => true, + ) { this.buttons.push([text, onclick, img, shown, enabled, "submenu"]); - return{}; + return {}; } - private makemenu(x: number, y: number, addinfo: x, other: y){ + private makemenu(x: number, y: number, addinfo: x, other: y) { const div = document.createElement("div"); div.classList.add("contextmenu", "flexttb"); let visibleButtons = 0; - for(const thing of this.buttons){ - if(!thing[3].call(addinfo, other))continue; + for (const thing of this.buttons) { + if (!thing[3].call(addinfo, other)) continue; visibleButtons++; const intext = document.createElement("button"); intext.disabled = !thing[4].call(addinfo, other); intext.classList.add("contextbutton"); - if(thing[0] instanceof Function){ + if (thing[0] instanceof Function) { intext.textContent = thing[0](); - }else{ + } else { intext.textContent = thing[0]; } console.log(thing); - if(thing[5] === "button" || thing[5] === "submenu"){ - intext.onclick = (e)=>{ + if (thing[5] === "button" || thing[5] === "submenu") { + intext.onclick = (e) => { div.remove(); - thing[1].call(addinfo, other,e) + thing[1].call(addinfo, other, e); }; } div.appendChild(intext); } - if(visibleButtons == 0)return; + if (visibleButtons == 0) return; - if(Contextmenu.currentmenu != ""){ + if (Contextmenu.currentmenu != "") { Contextmenu.currentmenu.remove(); } div.style.top = y + "px"; @@ -87,64 +87,74 @@ class Contextmenu{ Contextmenu.currentmenu = div; return this.div; } - bindContextmenu(obj: HTMLElement, addinfo: x, other: y,touchDrag:(x:number,y:number)=>unknown=()=>{},touchEnd:(x:number,y:number)=>unknown=()=>{}){ - const func = (event: MouseEvent)=>{ + bindContextmenu( + obj: HTMLElement, + addinfo: x, + other: y, + touchDrag: (x: number, y: number) => unknown = () => {}, + touchEnd: (x: number, y: number) => unknown = () => {}, + ) { + const func = (event: MouseEvent) => { event.preventDefault(); event.stopImmediatePropagation(); this.makemenu(event.clientX, event.clientY, addinfo, other); }; obj.addEventListener("contextmenu", func); - if(iOS){ - let hold:NodeJS.Timeout|undefined; - let x!:number; - let y!:number; - obj.addEventListener("touchstart",(event: TouchEvent)=>{ - x=event.touches[0].pageX; - y=event.touches[0].pageY; - if(event.touches.length > 1){ - event.preventDefault(); - event.stopImmediatePropagation(); - this.makemenu(event.touches[0].clientX, event.touches[0].clientY, addinfo, other); - }else{ - // - event.stopImmediatePropagation(); - hold=setTimeout(()=>{ - if(lastx**2+lasty**2>10**2) return; + if (iOS) { + let hold: NodeJS.Timeout | undefined; + let x!: number; + let y!: number; + obj.addEventListener( + "touchstart", + (event: TouchEvent) => { + x = event.touches[0].pageX; + y = event.touches[0].pageY; + if (event.touches.length > 1) { + event.preventDefault(); + event.stopImmediatePropagation(); this.makemenu(event.touches[0].clientX, event.touches[0].clientY, addinfo, other); - console.log(obj); - },500) - } - },{passive: false}); - let lastx=0; - let lasty=0; - obj.addEventListener("touchend",()=>{ - if(hold){ + } else { + // + event.stopImmediatePropagation(); + hold = setTimeout(() => { + if (lastx ** 2 + lasty ** 2 > 10 ** 2) return; + this.makemenu(event.touches[0].clientX, event.touches[0].clientY, addinfo, other); + console.log(obj); + }, 500); + } + }, + {passive: false}, + ); + let lastx = 0; + let lasty = 0; + obj.addEventListener("touchend", () => { + if (hold) { clearTimeout(hold); } - touchEnd(lastx,lasty); + touchEnd(lastx, lasty); }); - obj.addEventListener("touchmove",(event)=>{ - lastx=event.touches[0].pageX-x; - lasty=event.touches[0].pageY-y; - touchDrag(lastx,lasty); + obj.addEventListener("touchmove", (event) => { + lastx = event.touches[0].pageX - x; + lasty = event.touches[0].pageY - y; + touchDrag(lastx, lasty); }); } return func; } - static keepOnScreen(obj: HTMLElement){ + static keepOnScreen(obj: HTMLElement) { const html = document.documentElement.getBoundingClientRect(); const docheight = html.height; const docwidth = html.width; const box = obj.getBoundingClientRect(); console.log(box, docheight, docwidth); - if(box.right > docwidth){ + if (box.right > docwidth) { console.log("test"); obj.style.left = docwidth - box.width + "px"; } - if(box.bottom > docheight){ + if (box.bottom > docheight) { obj.style.top = docheight - box.height + "px"; } } } Contextmenu.setup(); -export{ Contextmenu }; +export {Contextmenu}; diff --git a/src/webpage/direct.ts b/src/webpage/direct.ts index 3cf2a18..f803bad 100644 --- a/src/webpage/direct.ts +++ b/src/webpage/direct.ts @@ -1,22 +1,22 @@ -import{ Guild }from"./guild.js"; -import{ Channel }from"./channel.js"; -import{ Message }from"./message.js"; -import{ Localuser }from"./localuser.js"; -import{ User }from"./user.js"; -import{channeljson,dirrectjson,memberjson,messagejson}from"./jsontypes.js"; -import{ Permissions }from"./permissions.js"; -import{ SnowFlake }from"./snowflake.js"; -import{ Contextmenu }from"./contextmenu.js"; -import { I18n } from "./i18n.js"; -import { Float, FormError } from "./settings.js"; +import {Guild} from "./guild.js"; +import {Channel} from "./channel.js"; +import {Message} from "./message.js"; +import {Localuser} from "./localuser.js"; +import {User} from "./user.js"; +import {channeljson, dirrectjson, memberjson, messagejson} from "./jsontypes.js"; +import {Permissions} from "./permissions.js"; +import {SnowFlake} from "./snowflake.js"; +import {Contextmenu} from "./contextmenu.js"; +import {I18n} from "./i18n.js"; +import {Float, FormError} from "./settings.js"; -class Direct extends Guild{ - declare channelids: { [key: string]: Group }; +class Direct extends Guild { + declare channelids: {[key: string]: Group}; channels: Group[]; - getUnixTime(): number{ + getUnixTime(): number { throw new Error("Do not call this for Direct, it does not make sense"); } - constructor(json: dirrectjson[], owner: Localuser){ + constructor(json: dirrectjson[], owner: Localuser) { super(-1, owner, null); this.message_notifications = 0; this.owner = owner; @@ -29,7 +29,7 @@ class Direct extends Guild{ this.roleids = new Map(); this.prevchannel = undefined; this.properties.name = I18n.getTranslation("DMs.name"); - for(const thing of json){ + for (const thing of json) { const temp = new Group(thing, this); this.channels.push(temp); this.channelids[temp.id] = temp; @@ -37,7 +37,7 @@ class Direct extends Guild{ } this.headchannels = this.channels; } - createChannelpac(json: any){ + createChannelpac(json: any) { const thischannel = new Group(json, this); this.channelids[thischannel.id] = thischannel; this.channels.push(thischannel); @@ -46,266 +46,270 @@ class Direct extends Guild{ this.printServers(); return thischannel; } - delChannel(json: channeljson){ + delChannel(json: channeljson) { const channel = this.channelids[json.id]; super.delChannel(json); - if(channel){ + if (channel) { channel.del(); } } - getHTML(){ - const ddiv=document.createElement("div"); - const build=super.getHTML(); - const freindDiv=document.createElement("div"); - freindDiv.classList.add("liststyle","flexltr","friendsbutton"); + getHTML() { + const ddiv = document.createElement("div"); + const build = super.getHTML(); + const freindDiv = document.createElement("div"); + freindDiv.classList.add("liststyle", "flexltr", "friendsbutton"); - const icon=document.createElement("span"); - icon.classList.add("svgicon","svg-friends","space"); + const icon = document.createElement("span"); + icon.classList.add("svgicon", "svg-friends", "space"); freindDiv.append(icon); freindDiv.append(I18n.getTranslation("friends.friends")); ddiv.append(freindDiv); - freindDiv.onclick=()=>{ + freindDiv.onclick = () => { this.loadChannel(null); - } + }; ddiv.append(build); return ddiv; } - noChannel(addstate:boolean){ - if(addstate){ - history.pushState([this.id,undefined], "", "/channels/" + this.id); + noChannel(addstate: boolean) { + if (addstate) { + history.pushState([this.id, undefined], "", "/channels/" + this.id); } this.localuser.pageTitle(I18n.getTranslation("friends.friendlist")); const channelTopic = document.getElementById("channelTopic") as HTMLSpanElement; channelTopic.removeAttribute("hidden"); - channelTopic.textContent=""; + channelTopic.textContent = ""; const loading = document.getElementById("loadingdiv") as HTMLDivElement; loading.classList.remove("loading"); this.localuser.getSidePannel(); const messages = document.getElementById("channelw") as HTMLDivElement; - for(const thing of Array.from(messages.getElementsByClassName("messagecontainer"))){ + for (const thing of Array.from(messages.getElementsByClassName("messagecontainer"))) { thing.remove(); } - const container=document.createElement("div"); - container.classList.add("messagecontainer","flexttb","friendcontainer") + const container = document.createElement("div"); + container.classList.add("messagecontainer", "flexttb", "friendcontainer"); messages.append(container); - const checkVoid=()=>{ - if(this.localuser.channelfocus!==undefined||this.localuser.lookingguild!==this){ - this.localuser.relationshipsUpdate=()=>{}; + const checkVoid = () => { + if (this.localuser.channelfocus !== undefined || this.localuser.lookingguild !== this) { + this.localuser.relationshipsUpdate = () => {}; } - } - function genuserstrip(user:User,icons:HTMLElement):HTMLElement{ - const div=document.createElement("div"); - div.classList.add("flexltr","liststyle"); + }; + function genuserstrip(user: User, icons: HTMLElement): HTMLElement { + const div = document.createElement("div"); + div.classList.add("flexltr", "liststyle"); user.bind(div); div.append(user.buildpfp()); - const userinfos=document.createElement("div"); + const userinfos = document.createElement("div"); userinfos.classList.add("flexttb"); - const username=document.createElement("span"); - username.textContent=user.name; - userinfos.append(username,user.getStatus()); + const username = document.createElement("span"); + username.textContent = user.name; + userinfos.append(username, user.getStatus()); div.append(userinfos); - User.contextmenu.bindContextmenu(div,user,undefined); - userinfos.style.flexGrow="1"; + User.contextmenu.bindContextmenu(div, user, undefined); + userinfos.style.flexGrow = "1"; div.append(icons); return div; } { //TODO update on users coming online - const online=document.createElement("button"); - online.textContent=I18n.getTranslation("friends.online"); + const online = document.createElement("button"); + online.textContent = I18n.getTranslation("friends.online"); channelTopic.append(online); - const genOnline=()=>{ - this.localuser.relationshipsUpdate=genOnline; + const genOnline = () => { + this.localuser.relationshipsUpdate = genOnline; checkVoid(); - container.innerHTML=""; + container.innerHTML = ""; container.append(I18n.getTranslation("friends.online:")); - for(const user of this.localuser.inrelation){ - if(user.relationshipType===1&&user.online){ - const buttonc=document.createElement("div"); - const button1=document.createElement("span"); - button1.classList.add("svg-frmessage","svgicon"); + for (const user of this.localuser.inrelation) { + if (user.relationshipType === 1 && user.online) { + const buttonc = document.createElement("div"); + const button1 = document.createElement("span"); + button1.classList.add("svg-frmessage", "svgicon"); buttonc.append(button1); buttonc.classList.add("friendlyButton"); - buttonc.onclick=(e)=>{ + buttonc.onclick = (e) => { e.stopImmediatePropagation(); user.opendm(); - } - container.append(genuserstrip(user,buttonc)); + }; + container.append(genuserstrip(user, buttonc)); } } - } - online.onclick=genOnline; + }; + online.onclick = genOnline; genOnline(); } { - const all=document.createElement("button"); - all.textContent=I18n.getTranslation("friends.all"); - const genAll=()=>{ - this.localuser.relationshipsUpdate=genAll; + const all = document.createElement("button"); + all.textContent = I18n.getTranslation("friends.all"); + const genAll = () => { + this.localuser.relationshipsUpdate = genAll; checkVoid(); - container.innerHTML=""; + container.innerHTML = ""; container.append(I18n.getTranslation("friends.all:")); - for(const user of this.localuser.inrelation){ - if(user.relationshipType===1){ - const buttonc=document.createElement("div"); - const button1=document.createElement("span"); - button1.classList.add("svg-frmessage","svgicon"); + for (const user of this.localuser.inrelation) { + if (user.relationshipType === 1) { + const buttonc = document.createElement("div"); + const button1 = document.createElement("span"); + button1.classList.add("svg-frmessage", "svgicon"); buttonc.append(button1); buttonc.classList.add("friendlyButton"); - buttonc.onclick=(e)=>{ + buttonc.onclick = (e) => { e.stopImmediatePropagation(); user.opendm(); - } - container.append(genuserstrip(user,buttonc)); + }; + container.append(genuserstrip(user, buttonc)); } } - } - all.onclick=genAll; + }; + all.onclick = genAll; channelTopic.append(all); } { - const pending=document.createElement("button"); - pending.textContent=I18n.getTranslation("friends.pending"); - const genPending=()=>{ - this.localuser.relationshipsUpdate=genPending; + const pending = document.createElement("button"); + pending.textContent = I18n.getTranslation("friends.pending"); + const genPending = () => { + this.localuser.relationshipsUpdate = genPending; checkVoid(); - container.innerHTML=""; + container.innerHTML = ""; container.append(I18n.getTranslation("friends.pending:")); - for(const user of this.localuser.inrelation){ - if(user.relationshipType===3||user.relationshipType===4){ - const buttons=document.createElement("div"); + for (const user of this.localuser.inrelation) { + if (user.relationshipType === 3 || user.relationshipType === 4) { + const buttons = document.createElement("div"); buttons.classList.add("flexltr"); - const buttonc=document.createElement("div"); - const button1=document.createElement("span"); - button1.classList.add("svgicon","svg-x"); - if(user.relationshipType===3){ - const buttonc=document.createElement("div"); - const button2=document.createElement("span"); - button2.classList.add("svgicon","svg-x"); + const buttonc = document.createElement("div"); + const button1 = document.createElement("span"); + button1.classList.add("svgicon", "svg-x"); + if (user.relationshipType === 3) { + const buttonc = document.createElement("div"); + const button2 = document.createElement("span"); + button2.classList.add("svgicon", "svg-x"); button2.classList.add("svg-addfriend"); buttonc.append(button2); buttonc.classList.add("friendlyButton"); buttonc.append(button2); buttons.append(buttonc); - buttonc.onclick=(e)=>{ + buttonc.onclick = (e) => { e.stopImmediatePropagation(); user.changeRelationship(1); outerDiv.remove(); - } + }; } buttonc.append(button1); buttonc.classList.add("friendlyButton"); - buttonc.onclick=(e)=>{ + buttonc.onclick = (e) => { e.stopImmediatePropagation(); user.changeRelationship(0); outerDiv.remove(); - } + }; buttons.append(buttonc); - const outerDiv=genuserstrip(user,buttons); + const outerDiv = genuserstrip(user, buttons); container.append(outerDiv); } } - } - pending.onclick=genPending; + }; + pending.onclick = genPending; channelTopic.append(pending); } { - const blocked=document.createElement("button"); - blocked.textContent=I18n.getTranslation("friends.blocked"); + const blocked = document.createElement("button"); + blocked.textContent = I18n.getTranslation("friends.blocked"); - const genBlocked=()=>{ - this.localuser.relationshipsUpdate=genBlocked; + const genBlocked = () => { + this.localuser.relationshipsUpdate = genBlocked; checkVoid(); - container.innerHTML=""; + container.innerHTML = ""; container.append(I18n.getTranslation("friends.blockedusers")); - for(const user of this.localuser.inrelation){ - if(user.relationshipType===2){ - const buttonc=document.createElement("div"); - const button1=document.createElement("span"); - button1.classList.add("svg-x","svgicon"); + for (const user of this.localuser.inrelation) { + if (user.relationshipType === 2) { + const buttonc = document.createElement("div"); + const button1 = document.createElement("span"); + button1.classList.add("svg-x", "svgicon"); buttonc.append(button1); buttonc.classList.add("friendlyButton"); - buttonc.onclick=(e)=>{ + buttonc.onclick = (e) => { user.changeRelationship(0); e.stopImmediatePropagation(); outerDiv.remove(); - } - const outerDiv=genuserstrip(user,buttonc); + }; + const outerDiv = genuserstrip(user, buttonc); container.append(outerDiv); } } - } - blocked.onclick=genBlocked; + }; + blocked.onclick = genBlocked; channelTopic.append(blocked); } { - const add=document.createElement("button"); - add.textContent=I18n.getTranslation("friends.addfriend"); - add.onclick=()=>{ - this.localuser.relationshipsUpdate=()=>{}; - container.innerHTML=""; - const float=new Float(""); - const options=float.options; - const form=options.addForm("",(e:any)=>{ - console.log(e); - if(e.code===404){ - throw new FormError(text,I18n.getTranslation("friends.notfound")); - }else if(e.code===400){ - throw new FormError(text,e.message.split("Error: ")[1]); - }else{ - const box=text.input.deref(); - if(!box)return; - box.value=""; - } - },{ - method:"POST", - fetchURL:this.info.api+"/users/@me/relationships", - headers:this.headers - }); - const text=form.addTextInput(I18n.getTranslation("friends.addfriendpromt"),"username"); - form.addPreprocessor((obj:any)=>{ - const [username,discriminator]=obj.username.split("#"); - obj.username=username; - obj.discriminator=discriminator; - if(!discriminator){ - throw new FormError(text,I18n.getTranslation("friends.discnotfound")); + const add = document.createElement("button"); + add.textContent = I18n.getTranslation("friends.addfriend"); + add.onclick = () => { + this.localuser.relationshipsUpdate = () => {}; + container.innerHTML = ""; + const float = new Float(""); + const options = float.options; + const form = options.addForm( + "", + (e: any) => { + console.log(e); + if (e.code === 404) { + throw new FormError(text, I18n.getTranslation("friends.notfound")); + } else if (e.code === 400) { + throw new FormError(text, e.message.split("Error: ")[1]); + } else { + const box = text.input.deref(); + if (!box) return; + box.value = ""; + } + }, + { + method: "POST", + fetchURL: this.info.api + "/users/@me/relationships", + headers: this.headers, + }, + ); + const text = form.addTextInput(I18n.getTranslation("friends.addfriendpromt"), "username"); + form.addPreprocessor((obj: any) => { + const [username, discriminator] = obj.username.split("#"); + obj.username = username; + obj.discriminator = discriminator; + if (!discriminator) { + throw new FormError(text, I18n.getTranslation("friends.discnotfound")); } }); container.append(float.generateHTML()); - } + }; channelTopic.append(add); } } - get mentions(){ - let mentions=0; - for(const thing of this.localuser.inrelation){ - if(thing.relationshipType===3){ - mentions+=1; + get mentions() { + let mentions = 0; + for (const thing of this.localuser.inrelation) { + if (thing.relationshipType === 3) { + mentions += 1; } } return mentions; } - giveMember(_member: memberjson){ + giveMember(_member: memberjson) { throw new Error("not a real guild, can't give member object"); } - getRole(/* ID: string */){ + getRole(/* ID: string */) { return null; } - hasRole(/* r: string */){ + hasRole(/* r: string */) { return false; } - isAdmin(){ + isAdmin() { return false; } - unreaddms(){ - for(const thing of this.channels){ + unreaddms() { + for (const thing of this.channels) { (thing as Group).unreads(); } } @@ -335,34 +339,46 @@ dmPermissions.setPermission("STREAM", 1); dmPermissions.setPermission("USE_VAD", 1); // @ts-ignore I need to look into this lol -class Group extends Channel{ +class Group extends Channel { user: User; static contextmenu = new Contextmenu("channel menu"); - static setupcontextmenu(){ - this.contextmenu.addbutton(()=>I18n.getTranslation("DMs.copyId"), function(this: Group){ - navigator.clipboard.writeText(this.id); - }); + static setupcontextmenu() { + this.contextmenu.addbutton( + () => I18n.getTranslation("DMs.copyId"), + function (this: Group) { + navigator.clipboard.writeText(this.id); + }, + ); - this.contextmenu.addbutton(()=>I18n.getTranslation("DMs.markRead"), function(this: Group){ - this.readbottom(); - }); + this.contextmenu.addbutton( + () => I18n.getTranslation("DMs.markRead"), + function (this: Group) { + this.readbottom(); + }, + ); - this.contextmenu.addbutton(()=>I18n.getTranslation("DMs.close"), function(this: Group){ - this.deleteChannel(); - }); + this.contextmenu.addbutton( + () => I18n.getTranslation("DMs.close"), + function (this: Group) { + this.deleteChannel(); + }, + ); - this.contextmenu.addbutton(()=>I18n.getTranslation("user.copyId"), function(){ - navigator.clipboard.writeText(this.user.id); - }); + this.contextmenu.addbutton( + () => I18n.getTranslation("user.copyId"), + function () { + navigator.clipboard.writeText(this.user.id); + }, + ); } - constructor(json: dirrectjson, owner: Direct){ + constructor(json: dirrectjson, owner: Direct) { super(-1, owner, json.id); this.owner = owner; this.headers = this.guild.headers; this.name = json.recipients[0]?.username; - if(json.recipients[0]){ + if (json.recipients[0]) { this.user = new User(json.recipients[0], this.localuser); - }else{ + } else { this.user = this.localuser.user; } this.name ??= this.localuser.user.username; @@ -376,26 +392,26 @@ class Group extends Channel{ this.setUpInfiniteScroller(); this.updatePosition(); } - updatePosition(){ - if(this.lastmessageid){ + updatePosition() { + if (this.lastmessageid) { this.position = SnowFlake.stringToUnixTime(this.lastmessageid); - }else{ + } else { this.position = 0; } this.position = -Math.max(this.position, this.getUnixTime()); } - createguildHTML(){ + createguildHTML() { const div = document.createElement("div"); - Group.contextmenu.bindContextmenu(div, this,undefined); + Group.contextmenu.bindContextmenu(div, this, undefined); this.html = new WeakRef(div); - div.classList.add("flexltr","liststyle"); + div.classList.add("flexltr", "liststyle"); const myhtml = document.createElement("span"); myhtml.classList.add("ellipsis"); myhtml.textContent = this.name; div.appendChild(this.user.buildpfp()); div.appendChild(myhtml); (div as any).myinfo = this; - div.onclick = _=>{ + div.onclick = (_) => { this.getHTML(); const toggle = document.getElementById("maintoggle") as HTMLInputElement; toggle.checked = true; @@ -403,56 +419,55 @@ class Group extends Channel{ return div; } - async getHTML(addstate=true){ + async getHTML(addstate = true) { const id = ++Channel.genid; - if(this.localuser.channelfocus){ + if (this.localuser.channelfocus) { this.localuser.channelfocus.infinite.delete(); } - if(this.guild !== this.localuser.lookingguild){ + if (this.guild !== this.localuser.lookingguild) { this.guild.loadGuild(); } this.guild.prevchannel = this; this.localuser.channelfocus = this; const prom = this.infinite.delete(); - if(addstate){ - history.pushState([this.guild_id,this.id], "", "/channels/" + this.guild_id + "/" + this.id); + if (addstate) { + history.pushState([this.guild_id, this.id], "", "/channels/" + this.guild_id + "/" + this.id); } this.localuser.pageTitle("@" + this.name); - (document.getElementById("channelTopic") as HTMLElement).setAttribute("hidden",""); + (document.getElementById("channelTopic") as HTMLElement).setAttribute("hidden", ""); const loading = document.getElementById("loadingdiv") as HTMLDivElement; Channel.regenLoadingMessages(); loading.classList.add("loading"); this.rendertyping(); - (document.getElementById("typebox") as HTMLDivElement).contentEditable ="" + true; - (document.getElementById("upload") as HTMLElement).style.visibility="visible"; - (document.getElementById("typediv") as HTMLElement).style.visibility="visible"; + (document.getElementById("typebox") as HTMLDivElement).contentEditable = "" + true; + (document.getElementById("upload") as HTMLElement).style.visibility = "visible"; + (document.getElementById("typediv") as HTMLElement).style.visibility = "visible"; (document.getElementById("typebox") as HTMLDivElement).focus(); await this.putmessages(); await prom; this.localuser.getSidePannel(); - if(id !== Channel.genid){ + if (id !== Channel.genid) { return; } this.buildmessages(); - } - messageCreate(messagep: { d: messagejson }){ + messageCreate(messagep: {d: messagejson}) { this.mentions++; const messagez = new Message(messagep.d, this); - if(this.lastmessageid){ + if (this.lastmessageid) { this.idToNext.set(this.lastmessageid, messagez.id); this.idToPrev.set(messagez.id, this.lastmessageid); } this.lastmessageid = messagez.id; - if(messagez.author === this.localuser.user){ + if (messagez.author === this.localuser.user) { this.lastreadmessageid = messagez.id; - if(this.myhtml){ + if (this.myhtml) { this.myhtml.classList.remove("cunread"); } - }else{ - if(this.myhtml){ + } else { + if (this.myhtml) { this.myhtml.classList.add("cunread"); } } @@ -460,55 +475,55 @@ class Group extends Channel{ this.updatePosition(); this.infinite.addedBottom(); this.guild.sortchannels(); - if(this.myhtml){ + if (this.myhtml) { const parrent = this.myhtml.parentElement as HTMLElement; parrent.prepend(this.myhtml); } - if(this === this.localuser.channelfocus){ - if(!this.infinitefocus){ + if (this === this.localuser.channelfocus) { + if (!this.infinitefocus) { this.tryfocusinfinate(); } this.infinite.addedBottom(); } this.unreads(); - if(messagez.author === this.localuser.user){ - this.mentions=0; + if (messagez.author === this.localuser.user) { + this.mentions = 0; return; } - if(this.localuser.lookingguild?.prevchannel === this && document.hasFocus()){ + if (this.localuser.lookingguild?.prevchannel === this && document.hasFocus()) { return; } - if(this.notification === "all"){ + if (this.notification === "all") { this.notify(messagez); - }else if(this.notification === "mentions" && messagez.mentionsuser(this.localuser.user)){ + } else if (this.notification === "mentions" && messagez.mentionsuser(this.localuser.user)) { this.notify(messagez); } } - notititle(message: Message){ + notititle(message: Message) { return message.author.username; } - readbottom(){ + readbottom() { super.readbottom(); this.unreads(); } all: WeakRef = new WeakRef(document.createElement("div")); noti: WeakRef = new WeakRef(document.createElement("div")); - del(){ + del() { const all = this.all.deref(); - if(all){ + if (all) { all.remove(); } - if(this.myhtml){ + if (this.myhtml) { this.myhtml.remove(); } } - unreads(){ + unreads() { const sentdms = document.getElementById("sentdms") as HTMLDivElement; //Need to change sometime const current = this.all.deref(); - if(this.hasunreads){ + if (this.hasunreads) { { const noti = this.noti.deref(); - if(noti){ + if (noti) { noti.textContent = this.mentions + ""; return; } @@ -525,24 +540,24 @@ class Group extends Channel{ buildpfp.classList.add("mentioned"); div.append(buildpfp); sentdms.append(div); - div.onclick = _=>{ + div.onclick = (_) => { this.guild.loadGuild(); this.getHTML(); const toggle = document.getElementById("maintoggle") as HTMLInputElement; toggle.checked = true; }; - }else if(current){ + } else if (current) { current.remove(); - }else{ + } else { } } - isAdmin(): boolean{ + isAdmin(): boolean { return false; } - hasPermission(name: string): boolean{ + hasPermission(name: string): boolean { return dmPermissions.hasPermission(name); } } -export{ Direct, Group }; +export {Direct, Group}; -Group.setupcontextmenu() +Group.setupcontextmenu(); diff --git a/src/webpage/disimg.ts b/src/webpage/disimg.ts index a1d2a1d..e37978e 100644 --- a/src/webpage/disimg.ts +++ b/src/webpage/disimg.ts @@ -1,37 +1,37 @@ -class ImagesDisplay{ - images:string[]; - index=0; - constructor(srcs:string[],index=0){ - this.images=srcs; - this.index=index; +class ImagesDisplay { + images: string[]; + index = 0; + constructor(srcs: string[], index = 0) { + this.images = srcs; + this.index = index; } - weakbg=new WeakRef(document.createElement("div")); - get background():HTMLElement|undefined{ + weakbg = new WeakRef(document.createElement("div")); + get background(): HTMLElement | undefined { return this.weakbg.deref(); } - set background(e:HTMLElement){ - this.weakbg=new WeakRef(e); + set background(e: HTMLElement) { + this.weakbg = new WeakRef(e); } - makeHTML():HTMLElement{ + makeHTML(): HTMLElement { //TODO this should be able to display more than one image at a time lol - const image= document.createElement("img"); - image.src=this.images[this.index]; - image.classList.add("imgfit","centeritem"); + const image = document.createElement("img"); + image.src = this.images[this.index]; + image.classList.add("imgfit", "centeritem"); return image; } - show(){ + show() { this.background = document.createElement("div"); this.background.classList.add("background"); this.background.appendChild(this.makeHTML()); - this.background.onclick = _=>{ + this.background.onclick = (_) => { this.hide(); }; document.body.append(this.background); } - hide(){ - if(this.background){ + hide() { + if (this.background) { this.background.remove(); } } } -export{ImagesDisplay} +export {ImagesDisplay}; diff --git a/src/webpage/embed.ts b/src/webpage/embed.ts index 73bad4a..50dd681 100644 --- a/src/webpage/embed.ts +++ b/src/webpage/embed.ts @@ -1,94 +1,85 @@ -import{ Message }from"./message.js"; -import{ MarkDown }from"./markdown.js"; -import{ embedjson, invitejson }from"./jsontypes.js"; -import{ getapiurls, getInstances }from"./utils/utils.js"; -import{ Guild }from"./guild.js"; -import { I18n } from "./i18n.js"; -import { ImagesDisplay } from "./disimg.js"; +import {Message} from "./message.js"; +import {MarkDown} from "./markdown.js"; +import {embedjson, invitejson} from "./jsontypes.js"; +import {getapiurls, getInstances} from "./utils/utils.js"; +import {Guild} from "./guild.js"; +import {I18n} from "./i18n.js"; +import {ImagesDisplay} from "./disimg.js"; -class Embed{ +class Embed { type: string; owner: Message; json: embedjson; - constructor(json: embedjson, owner: Message){ + constructor(json: embedjson, owner: Message) { this.type = this.getType(json); this.owner = owner; this.json = json; } - getType(json: embedjson){ + getType(json: embedjson) { const instances = getInstances(); - if( - instances && -json.type === "link" && -json.url && -URL.canParse(json.url) - ){ + if (instances && json.type === "link" && json.url && URL.canParse(json.url)) { const Url = new URL(json.url); - for(const instance of instances){ - if(instance.url && URL.canParse(instance.url)){ + for (const instance of instances) { + if (instance.url && URL.canParse(instance.url)) { const IUrl = new URL(instance.url); const params = new URLSearchParams(Url.search); let host: string; - if(params.has("instance")){ + if (params.has("instance")) { const url = params.get("instance") as string; - if(URL.canParse(url)){ + if (URL.canParse(url)) { host = new URL(url).host; - }else{ + } else { host = Url.host; } - }else{ + } else { host = Url.host; } - if(IUrl.host === host){ - const code = -Url.pathname.split("/")[Url.pathname.split("/").length - 1]; + if (IUrl.host === host) { + const code = Url.pathname.split("/")[Url.pathname.split("/").length - 1]; json.invite = { url: instance.url, code, }; - return"invite"; + return "invite"; } } } } return json.type || "rich"; } - generateHTML(){ - switch(this.type){ - case"rich": - return this.generateRich(); - case"image": - return this.generateImage(); - case"invite": - return this.generateInvite(); - case"link": - return this.generateLink(); - case"video": - case"article": - return this.generateArticle(); - default: - console.warn( - `unsupported embed type ${this.type}, please add support dev :3`, - this.json - ); - return document.createElement("div"); //prevent errors by giving blank div + generateHTML() { + switch (this.type) { + case "rich": + return this.generateRich(); + case "image": + return this.generateImage(); + case "invite": + return this.generateInvite(); + case "link": + return this.generateLink(); + case "video": + case "article": + return this.generateArticle(); + default: + console.warn(`unsupported embed type ${this.type}, please add support dev :3`, this.json); + return document.createElement("div"); //prevent errors by giving blank div } } - get message(){ + get message() { return this.owner; } - get channel(){ + get channel() { return this.message.channel; } - get guild(){ + get guild() { return this.channel.guild; } - get localuser(){ + get localuser() { return this.guild.localuser; } - generateRich(){ + generateRich() { const div = document.createElement("div"); - if(this.json.color){ + if (this.json.color) { div.style.backgroundColor = "#" + this.json.color.toString(16); } div.classList.add("embed-color"); @@ -97,9 +88,9 @@ Url.pathname.split("/")[Url.pathname.split("/").length - 1]; embed.classList.add("embed"); div.append(embed); - if(this.json.author){ + if (this.json.author) { const authorline = document.createElement("div"); - if(this.json.author.icon_url){ + if (this.json.author.icon_url) { const img = document.createElement("img"); img.classList.add("embedimg"); img.src = this.json.author.icon_url; @@ -107,31 +98,31 @@ Url.pathname.split("/")[Url.pathname.split("/").length - 1]; } const a = document.createElement("a"); a.textContent = this.json.author.name as string; - if(this.json.author.url){ + if (this.json.author.url) { MarkDown.safeLink(a, this.json.author.url); } a.classList.add("username"); authorline.append(a); embed.append(authorline); } - if(this.json.title){ + if (this.json.title) { const title = document.createElement("a"); title.append(new MarkDown(this.json.title, this.channel).makeHTML()); - if(this.json.url){ + if (this.json.url) { MarkDown.safeLink(title, this.json.url); } title.classList.add("embedtitle"); embed.append(title); } - if(this.json.description){ + if (this.json.description) { const p = document.createElement("p"); p.append(new MarkDown(this.json.description, this.channel).makeHTML()); embed.append(p); } embed.append(document.createElement("br")); - if(this.json.fields){ - for(const thing of this.json.fields){ + if (this.json.fields) { + for (const thing of this.json.fields) { const div = document.createElement("div"); const b = document.createElement("b"); b.textContent = thing.name; @@ -141,31 +132,31 @@ Url.pathname.split("/")[Url.pathname.split("/").length - 1]; p.classList.add("embedp"); div.append(p); - if(thing.inline){ + if (thing.inline) { div.classList.add("inline"); } embed.append(div); } } - if(this.json.footer || this.json.timestamp){ + if (this.json.footer || this.json.timestamp) { const footer = document.createElement("div"); - if(this.json?.footer?.icon_url){ + if (this.json?.footer?.icon_url) { const img = document.createElement("img"); img.src = this.json.footer.icon_url; img.classList.add("embedicon"); footer.append(img); } - if(this.json?.footer?.text){ + if (this.json?.footer?.text) { const span = document.createElement("span"); span.textContent = this.json.footer.text; footer.append(span); } - if(this.json?.footer && this.json?.timestamp){ + if (this.json?.footer && this.json?.timestamp) { const span = document.createElement("span"); span.textContent = " • "; footer.append(span); } - if(this.json?.timestamp){ + if (this.json?.timestamp) { const span = document.createElement("span"); span.textContent = new Date(this.json.timestamp).toLocaleString(); footer.append(span); @@ -174,15 +165,15 @@ Url.pathname.split("/")[Url.pathname.split("/").length - 1]; } return div; } - generateImage(){ + generateImage() { const img = document.createElement("img"); img.classList.add("messageimg"); - img.onclick = function(){ + img.onclick = function () { const full = new ImagesDisplay([img.src]); full.show(); }; img.src = this.json.thumbnail.proxy_url; - if(this.json.thumbnail.width){ + if (this.json.thumbnail.width) { let scale = 1; const max = 96 * 3; scale = Math.max(scale, this.json.thumbnail.width / max); @@ -195,12 +186,12 @@ Url.pathname.split("/")[Url.pathname.split("/").length - 1]; console.log(this.json, "Image fix"); return img; } - generateLink(){ + generateLink() { const table = document.createElement("table"); table.classList.add("embed", "linkembed"); const trtop = document.createElement("tr"); table.append(trtop); - if(this.json.url && this.json.title){ + if (this.json.url && this.json.title) { const td = document.createElement("td"); const a = document.createElement("a"); MarkDown.safeLink(a, this.json.url); @@ -211,9 +202,9 @@ Url.pathname.split("/")[Url.pathname.split("/").length - 1]; { const td = document.createElement("td"); const img = document.createElement("img"); - if(this.json.thumbnail){ + if (this.json.thumbnail) { img.classList.add("embedimg"); - img.onclick = function(){ + img.onclick = function () { const full = new ImagesDisplay([img.src]); full.show(); }; @@ -224,7 +215,7 @@ Url.pathname.split("/")[Url.pathname.split("/").length - 1]; } const bottomtr = document.createElement("tr"); const td = document.createElement("td"); - if(this.json.description){ + if (this.json.description) { const span = document.createElement("span"); span.textContent = this.json.description; td.append(span); @@ -233,59 +224,62 @@ Url.pathname.split("/")[Url.pathname.split("/").length - 1]; table.append(bottomtr); return table; } - invcache: [invitejson, { cdn: string; api: string }] | undefined; - generateInvite(){ - if(this.invcache && (!this.json.invite || !this.localuser)){ + invcache: [invitejson, {cdn: string; api: string}] | undefined; + generateInvite() { + if (this.invcache && (!this.json.invite || !this.localuser)) { return this.generateLink(); } const div = document.createElement("div"); div.classList.add("embed", "inviteEmbed", "flexttb"); const json1 = this.json.invite; - (async ()=>{ + (async () => { let json: invitejson; - let info: { cdn: string; api: string }; - if(!this.invcache){ - if(!json1){ - div.classList.remove("embed", "inviteEmbed", "flexttb") + let info: {cdn: string; api: string}; + if (!this.invcache) { + if (!json1) { + div.classList.remove("embed", "inviteEmbed", "flexttb"); div.append(this.generateLink()); return; } const tempinfo = await getapiurls(json1.url); - if(!tempinfo){ - div.classList.remove("embed", "inviteEmbed", "flexttb") + if (!tempinfo) { + div.classList.remove("embed", "inviteEmbed", "flexttb"); div.append(this.generateLink()); return; } info = tempinfo; const res = await fetch(info.api + "/invites/" + json1.code); - if(!res.ok){ - div.classList.remove("embed", "inviteEmbed", "flexttb") + if (!res.ok) { + div.classList.remove("embed", "inviteEmbed", "flexttb"); div.append(this.generateLink()); return; } json = (await res.json()) as invitejson; this.invcache = [json, info]; - }else{ + } else { [json, info] = this.invcache; } - if(!json){ + if (!json) { div.append(this.generateLink()); - div.classList.remove("embed", "inviteEmbed", "flexttb") + div.classList.remove("embed", "inviteEmbed", "flexttb"); return; } - if(json.guild.banner){ + if (json.guild.banner) { const banner = document.createElement("img"); - banner.src = this.localuser.info.cdn + "/icons/" + json.guild.id + "/" + json.guild.banner + ".png?size=256"; + banner.src = + this.localuser.info.cdn + + "/icons/" + + json.guild.id + + "/" + + json.guild.banner + + ".png?size=256"; banner.classList.add("banner"); div.append(banner); } - const guild: invitejson["guild"] & { info?: { cdn: string } } = -json.guild; + const guild: invitejson["guild"] & {info?: {cdn: string}} = json.guild; guild.info = info; - const icon = Guild.generateGuildIcon( -guild as invitejson["guild"] & { info: { cdn: string } } - ); + const icon = Guild.generateGuildIcon(guild as invitejson["guild"] & {info: {cdn: string}}); const iconrow = document.createElement("div"); iconrow.classList.add("flexltr"); iconrow.append(icon); @@ -305,30 +299,30 @@ guild as invitejson["guild"] & { info: { cdn: string } } div.append(iconrow); const h2 = document.createElement("h2"); - h2.textContent = I18n.getTranslation("invite.invitedBy",json.inviter.username); + h2.textContent = I18n.getTranslation("invite.invitedBy", json.inviter.username); div.append(h2); const button = document.createElement("button"); button.textContent = I18n.getTranslation("invite.accept"); - if(this.localuser.info.api.startsWith(info.api) && this.localuser.guildids.has(guild.id)){ + if (this.localuser.info.api.startsWith(info.api) && this.localuser.guildids.has(guild.id)) { button.textContent = I18n.getTranslation("invite.alreadyJoined"); button.disabled = true; } button.classList.add("acceptinvbutton"); div.append(button); - button.onclick = _=>{ - if(this.localuser.info.api.startsWith(info.api)){ + button.onclick = (_) => { + if (this.localuser.info.api.startsWith(info.api)) { fetch(this.localuser.info.api + "/invites/" + json.code, { method: "POST", headers: this.localuser.headers, }) - .then(r=>r.json()) - .then(_=>{ - if(_.message){ + .then((r) => r.json()) + .then((_) => { + if (_.message) { alert(_.message); } }); - }else{ - if(this.json.invite){ + } else { + if (this.json.invite) { const params = new URLSearchParams(""); params.set("instance", this.json.invite.url); const encoded = params.toString(); @@ -340,33 +334,33 @@ guild as invitejson["guild"] & { info: { cdn: string } } })(); return div; } - generateArticle(){ + generateArticle() { const colordiv = document.createElement("div"); colordiv.style.backgroundColor = "#000000"; colordiv.classList.add("embed-color"); const div = document.createElement("div"); div.classList.add("embed"); - if(this.json.provider){ + if (this.json.provider) { const provider = document.createElement("p"); provider.classList.add("provider"); provider.textContent = this.json.provider.name; div.append(provider); } const a = document.createElement("a"); - if(this.json.url && this.json.url){ + if (this.json.url && this.json.url) { MarkDown.safeLink(a, this.json.url); a.textContent = this.json.url; div.append(a); } - if(this.json.description){ + if (this.json.description) { const description = document.createElement("p"); description.textContent = this.json.description; div.append(description); } - if(this.json.thumbnail){ + if (this.json.thumbnail) { const img = document.createElement("img"); - if(this.json.thumbnail.width && this.json.thumbnail.width){ + if (this.json.thumbnail.width && this.json.thumbnail.width) { let scale = 1; const inch = 96; scale = Math.max(scale, this.json.thumbnail.width / inch / 4); @@ -377,21 +371,21 @@ guild as invitejson["guild"] & { info: { cdn: string } } img.style.height = this.json.thumbnail.height + "px"; } img.classList.add("bigembedimg"); - if(this.json.video){ - img.onclick = async ()=>{ - if(this.json.video){ + if (this.json.video) { + img.onclick = async () => { + if (this.json.video) { img.remove(); const iframe = document.createElement("iframe"); iframe.src = this.json.video.url + "?autoplay=1"; - if(this.json.thumbnail.width && this.json.thumbnail.width){ + if (this.json.thumbnail.width && this.json.thumbnail.width) { iframe.style.width = this.json.thumbnail.width + "px"; iframe.style.height = this.json.thumbnail.height + "px"; } div.append(iframe); } }; - }else{ - img.onclick = async ()=>{ + } else { + img.onclick = async () => { const full = new ImagesDisplay([img.src]); full.show(); }; @@ -403,4 +397,4 @@ guild as invitejson["guild"] & { info: { cdn: string } } return colordiv; } } -export{ Embed }; +export {Embed}; diff --git a/src/webpage/emoji.ts b/src/webpage/emoji.ts index b6e04e8..bff6a99 100644 --- a/src/webpage/emoji.ts +++ b/src/webpage/emoji.ts @@ -1,75 +1,73 @@ -import{ Contextmenu }from"./contextmenu.js"; -import{ Guild }from"./guild.js"; -import { emojijson } from "./jsontypes.js"; -import{ Localuser }from"./localuser.js"; -import { BinRead } from "./utils/binaryUtils.js"; +import {Contextmenu} from "./contextmenu.js"; +import {Guild} from "./guild.js"; +import {emojijson} from "./jsontypes.js"; +import {Localuser} from "./localuser.js"; +import {BinRead} from "./utils/binaryUtils.js"; //I need to recompile the emoji format for translation -class Emoji{ +class Emoji { static emojis: { name: string; emojis: { - name: string; - emoji: string; + name: string; + emoji: string; }[]; }[]; name: string; id?: string; - emoji?:string; + emoji?: string; animated: boolean; owner: Guild | Localuser; - get guild(){ - if(this.owner instanceof Guild){ + get guild() { + if (this.owner instanceof Guild) { return this.owner; } return null; } - get localuser(){ - if(this.owner instanceof Guild){ + get localuser() { + if (this.owner instanceof Guild) { return this.owner.localuser; - }else{ + } else { return this.owner; } } - get info(){ + get info() { return this.owner.info; } - constructor( - json: emojijson, - owner: Guild | Localuser - ){ + constructor(json: emojijson, owner: Guild | Localuser) { this.name = json.name; this.id = json.id; - this.animated = json.animated||false; + this.animated = json.animated || false; this.owner = owner; - this.emoji=json.emoji; + this.emoji = json.emoji; } - getHTML(bigemoji: boolean = false){ - if(this.id){ + getHTML(bigemoji: boolean = false) { + if (this.id) { const emojiElem = document.createElement("img"); emojiElem.classList.add("md-emoji"); emojiElem.classList.add(bigemoji ? "bigemoji" : "smallemoji"); emojiElem.crossOrigin = "anonymous"; - emojiElem.src =this.info.cdn+"/emojis/"+this.id+"."+(this.animated ? "gif" : "png")+"?size=32"; + emojiElem.src = + this.info.cdn + "/emojis/" + this.id + "." + (this.animated ? "gif" : "png") + "?size=32"; emojiElem.alt = this.name; emojiElem.loading = "lazy"; return emojiElem; - }else if(this.emoji){ + } else if (this.emoji) { const emojiElem = document.createElement("span"); emojiElem.classList.add("md-emoji"); emojiElem.classList.add(bigemoji ? "bigemoji" : "smallemoji"); - emojiElem.textContent=this.emoji; + emojiElem.textContent = this.emoji; return emojiElem; - }else{ + } else { throw new Error("This path should *never* be gone down, this means a malformed emoji"); } } - static decodeEmojiList(buffer: ArrayBuffer){ - const reader=new BinRead(buffer) - const build: { name: string; emojis: { name: string; emoji: string }[] }[] = []; + static decodeEmojiList(buffer: ArrayBuffer) { + const reader = new BinRead(buffer); + const build: {name: string; emojis: {name: string; emoji: string}[]}[] = []; let cats = reader.read16(); - for(; cats !== 0; cats--){ + for (; cats !== 0; cats--) { const name = reader.readString16(); const emojis: { name: string; @@ -77,7 +75,7 @@ class Emoji{ emoji: string; }[] = []; let emojinumber = reader.read16(); - for(; emojinumber !== 0; emojinumber--){ + for (; emojinumber !== 0; emojinumber--) { //console.log(emojis); const name = reader.readString8(); const len = reader.read8(); @@ -96,22 +94,18 @@ class Emoji{ } this.emojis = build; } - static grabEmoji(){ + static grabEmoji() { fetch("/emoji.bin") - .then(e=>{ + .then((e) => { return e.arrayBuffer(); }) - .then(e=>{ + .then((e) => { Emoji.decodeEmojiList(e); }); } - static async emojiPicker( - x: number, - y: number, - localuser: Localuser - ): Promise{ + static async emojiPicker(x: number, y: number, localuser: Localuser): Promise { let res: (r: Emoji | string) => void; - const promise: Promise = new Promise(r=>{ + const promise: Promise = new Promise((r) => { res = r; }); const menu = document.createElement("div"); @@ -130,33 +124,39 @@ class Emoji{ let isFirst = true; localuser.guilds - .filter(guild=>guild.id != "@me" && guild.emojis.length > 0) - .forEach(guild=>{ + .filter((guild) => guild.id != "@me" && guild.emojis.length > 0) + .forEach((guild) => { const select = document.createElement("div"); select.classList.add("emojiSelect"); - if(guild.properties.icon){ + if (guild.properties.icon) { const img = document.createElement("img"); img.classList.add("pfp", "servericon", "emoji-server"); img.crossOrigin = "anonymous"; - img.src = localuser.info.cdn+"/icons/"+guild.properties.id+"/"+guild.properties.icon+".png?size=48"; + img.src = + localuser.info.cdn + + "/icons/" + + guild.properties.id + + "/" + + guild.properties.icon + + ".png?size=48"; img.alt = "Server: " + guild.properties.name; select.appendChild(img); - }else{ + } else { const div = document.createElement("span"); div.textContent = guild.properties.name .replace(/'s /g, " ") - .replace(/\w+/g, word=>word[0]) + .replace(/\w+/g, (word) => word[0]) .replace(/\s/g, ""); select.append(div); } selection.append(select); - const clickEvent = ()=>{ + const clickEvent = () => { title.textContent = guild.properties.name; body.innerHTML = ""; - for(const emojit of guild.emojis){ + for (const emojit of guild.emojis) { const emojiElem = document.createElement("div"); emojiElem.classList.add("emojiSelect"); @@ -166,14 +166,14 @@ class Emoji{ name: emojit.name, animated: emojit.animated as boolean, }, - localuser + localuser, ); emojiElem.append(emojiClass.getHTML()); body.append(emojiElem); - emojiElem.addEventListener("click", ()=>{ + emojiElem.addEventListener("click", () => { res(emojiClass); - if(Contextmenu.currentmenu !== ""){ + if (Contextmenu.currentmenu !== "") { Contextmenu.currentmenu.remove(); } }); @@ -181,14 +181,14 @@ class Emoji{ }; select.addEventListener("click", clickEvent); - if(isFirst){ + if (isFirst) { clickEvent(); isFirst = false; } }); - setTimeout(()=>{ - if(Contextmenu.currentmenu != ""){ + setTimeout(() => { + if (Contextmenu.currentmenu != "") { Contextmenu.currentmenu.remove(); } document.body.append(menu); @@ -197,29 +197,29 @@ class Emoji{ }, 10); let i = 0; - for(const thing of Emoji.emojis){ + for (const thing of Emoji.emojis) { const select = document.createElement("div"); select.textContent = thing.emojis[0].emoji; select.classList.add("emojiSelect"); selection.append(select); - const clickEvent = ()=>{ + const clickEvent = () => { title.textContent = thing.name; body.innerHTML = ""; - for(const emojit of thing.emojis){ + for (const emojit of thing.emojis) { const emoji = document.createElement("div"); emoji.classList.add("emojiSelect"); emoji.textContent = emojit.emoji; body.append(emoji); - emoji.onclick = _=>{ + emoji.onclick = (_) => { res(emojit.emoji); - if(Contextmenu.currentmenu !== ""){ + if (Contextmenu.currentmenu !== "") { Contextmenu.currentmenu.remove(); } }; } }; select.onclick = clickEvent; - if(i === 0){ + if (i === 0) { clickEvent(); } i++; @@ -228,40 +228,39 @@ class Emoji{ menu.append(body); return promise; } - static searchEmoji(search:string,localuser:Localuser,results=50):[Emoji,number][]{ - const ranked:[emojijson,number][]=[]; - function similar(json:emojijson){ - if(json.name.includes(search)){ - ranked.push([json,search.length/json.name.length]); + static searchEmoji(search: string, localuser: Localuser, results = 50): [Emoji, number][] { + const ranked: [emojijson, number][] = []; + function similar(json: emojijson) { + if (json.name.includes(search)) { + ranked.push([json, search.length / json.name.length]); return true; - }else if(json.name.toLowerCase().includes(search.toLowerCase())){ - ranked.push([json,search.length/json.name.length/1.4]); + } else if (json.name.toLowerCase().includes(search.toLowerCase())) { + ranked.push([json, search.length / json.name.length / 1.4]); return true; - }else{ + } else { return false; } } - for(const group of this.emojis){ - for(const emoji of group.emojis){ - similar(emoji) + for (const group of this.emojis) { + for (const emoji of group.emojis) { + similar(emoji); } } - const weakGuild=new WeakMap(); - for(const guild of localuser.guilds){ - if(guild.id!=="@me"&&guild.emojis.length!==0){ - for(const emoji of guild.emojis){ - if(similar(emoji)){ - weakGuild.set(emoji,guild); - }; + const weakGuild = new WeakMap(); + for (const guild of localuser.guilds) { + if (guild.id !== "@me" && guild.emojis.length !== 0) { + for (const emoji of guild.emojis) { + if (similar(emoji)) { + weakGuild.set(emoji, guild); + } } } } - ranked.sort((a,b)=>b[1]-a[1]); - return ranked.splice(0,results).map(a=>{ - return [new Emoji(a[0],weakGuild.get(a[0])||localuser),a[1]]; - - }) + ranked.sort((a, b) => b[1] - a[1]); + return ranked.splice(0, results).map((a) => { + return [new Emoji(a[0], weakGuild.get(a[0]) || localuser), a[1]]; + }); } } Emoji.grabEmoji(); -export{ Emoji }; +export {Emoji}; diff --git a/src/webpage/file.ts b/src/webpage/file.ts index a16dcb5..f394ad4 100644 --- a/src/webpage/file.ts +++ b/src/webpage/file.ts @@ -1,8 +1,8 @@ -import{ Message }from"./message.js"; -import{ filejson }from"./jsontypes.js"; -import { ImagesDisplay } from "./disimg.js"; +import {Message} from "./message.js"; +import {filejson} from "./jsontypes.js"; +import {ImagesDisplay} from "./disimg.js"; -class File{ +class File { owner: Message | null; id: string; filename: string; @@ -12,7 +12,7 @@ class File{ proxy_url: string | undefined; url: string; size: number; - constructor(fileJSON: filejson, owner: Message | null){ + constructor(fileJSON: filejson, owner: Message | null) { this.owner = owner; this.id = fileJSON.id; this.filename = fileJSON.filename; @@ -24,9 +24,9 @@ class File{ this.content_type = fileJSON.content_type; this.size = fileJSON.size; } - getHTML(temp: boolean = false): HTMLElement{ + getHTML(temp: boolean = false): HTMLElement { const src = this.proxy_url || this.url; - if(this.width && this.height){ + if (this.width && this.height) { let scale = 1; const max = 96 * 3; scale = Math.max(scale, this.width / max); @@ -34,35 +34,35 @@ class File{ this.width /= scale; this.height /= scale; } - if(this.content_type.startsWith("image/")){ + if (this.content_type.startsWith("image/")) { const div = document.createElement("div"); const img = document.createElement("img"); img.classList.add("messageimg"); div.classList.add("messageimgdiv"); - img.onclick = function(){ + img.onclick = function () { const full = new ImagesDisplay([img.src]); full.show(); }; img.src = src; div.append(img); - if(this.width){ + if (this.width) { div.style.width = this.width + "px"; div.style.height = this.height + "px"; } return div; - }else if(this.content_type.startsWith("video/")){ + } else if (this.content_type.startsWith("video/")) { const video = document.createElement("video"); const source = document.createElement("source"); source.src = src; video.append(source); source.type = this.content_type; video.controls = !temp; - if(this.width && this.height){ + if (this.width && this.height) { video.width = this.width; video.height = this.height; } return video; - }else if(this.content_type.startsWith("audio/")){ + } else if (this.content_type.startsWith("audio/")) { const audio = document.createElement("audio"); const source = document.createElement("source"); source.src = src; @@ -70,11 +70,11 @@ class File{ source.type = this.content_type; audio.controls = !temp; return audio; - }else{ + } else { return this.createunknown(); } } - upHTML(files: Blob[], file: globalThis.File): HTMLElement{ + upHTML(files: Blob[], file: globalThis.File): HTMLElement { const div = document.createElement("div"); const contained = this.getHTML(true); div.classList.add("containedFile"); @@ -82,9 +82,9 @@ class File{ const controls = document.createElement("div"); const garbage = document.createElement("button"); const icon = document.createElement("span"); - icon.classList.add("svgicon","svg-delete"); + icon.classList.add("svgicon", "svg-delete"); garbage.append(icon); - garbage.onclick = _=>{ + garbage.onclick = (_) => { div.remove(); files.splice(files.indexOf(file), 1); }; @@ -93,7 +93,7 @@ class File{ controls.append(garbage); return div; } - static initFromBlob(file: globalThis.File){ + static initFromBlob(file: globalThis.File) { return new File( { filename: file.name, @@ -105,10 +105,10 @@ class File{ url: URL.createObjectURL(file), proxy_url: undefined, }, - null + null, ); } - createunknown(): HTMLElement{ + createunknown(): HTMLElement { console.log("🗎"); const src = this.proxy_url || this.url; const div = document.createElement("table"); @@ -121,12 +121,12 @@ class File{ fileicon.classList.add("fileicon"); fileicon.rowSpan = 2; const nametd = document.createElement("td"); - if(src){ + if (src) { const a = document.createElement("a"); a.href = src; a.textContent = this.filename; nametd.append(a); - }else{ + } else { nametd.textContent = this.filename; } @@ -140,11 +140,13 @@ class File{ div.appendChild(sizetr); return div; } - static filesizehuman(fsize: number){ + static filesizehuman(fsize: number) { const i = fsize == 0 ? 0 : Math.floor(Math.log(fsize) / Math.log(1024)); - return( - Number((fsize / Math.pow(1024, i)).toFixed(2)) * 1 + " " + ["Bytes", "Kilobytes", "Megabytes", "Gigabytes", "Terabytes"][i] // I don't think this changes across languages, correct me if I'm wrong + return ( + Number((fsize / Math.pow(1024, i)).toFixed(2)) * 1 + + " " + + ["Bytes", "Kilobytes", "Megabytes", "Gigabytes", "Terabytes"][i] // I don't think this changes across languages, correct me if I'm wrong ); } } -export{ File }; +export {File}; diff --git a/src/webpage/guild.ts b/src/webpage/guild.ts index c217cb7..1cd544f 100644 --- a/src/webpage/guild.ts +++ b/src/webpage/guild.ts @@ -1,17 +1,24 @@ -import{ Channel }from"./channel.js"; -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{ Permissions }from"./permissions.js"; -import{ SnowFlake }from"./snowflake.js"; -import{channeljson,guildjson,emojijson,memberjson,invitejson,rolesjson, emojipjson,}from"./jsontypes.js"; -import{ User }from"./user.js"; -import { I18n } from "./i18n.js"; -import { Emoji } from "./emoji.js"; +import {Channel} from "./channel.js"; +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 {Permissions} from "./permissions.js"; +import {SnowFlake} from "./snowflake.js"; +import { + channeljson, + guildjson, + memberjson, + invitejson, + rolesjson, + emojipjson, +} from "./jsontypes.js"; +import {User} from "./user.js"; +import {I18n} from "./i18n.js"; +import {Emoji} from "./emoji.js"; -class Guild extends SnowFlake{ +class Guild extends SnowFlake { owner!: Localuser; headers!: Localuser["headers"]; channels!: Channel[]; @@ -29,68 +36,82 @@ class Guild extends SnowFlake{ html!: HTMLElement; emojis!: emojipjson[]; large!: boolean; - members=new Set(); + members = new Set(); static contextmenu = new Contextmenu("guild menu"); - static setupcontextmenu(){ - Guild.contextmenu.addbutton(()=>I18n.getTranslation("guild.copyId"), function(this: Guild){ - navigator.clipboard.writeText(this.id); - }); - - Guild.contextmenu.addbutton(()=>I18n.getTranslation("guild.markRead"), function(this: Guild){ - this.markAsRead(); - }); - - Guild.contextmenu.addbutton(()=>I18n.getTranslation("guild.notifications"), function(this: Guild){ - this.setnotifcation(); - }); - - this.contextmenu.addbutton( - ()=>I18n.getTranslation("user.editServerProfile"), - function(){ - this.member.showEditProfile(); - } + static setupcontextmenu() { + Guild.contextmenu.addbutton( + () => I18n.getTranslation("guild.copyId"), + function (this: Guild) { + navigator.clipboard.writeText(this.id); + }, ); Guild.contextmenu.addbutton( - ()=>I18n.getTranslation("guild.leave"), - function(this: Guild){ + () => I18n.getTranslation("guild.markRead"), + function (this: Guild) { + this.markAsRead(); + }, + ); + + Guild.contextmenu.addbutton( + () => I18n.getTranslation("guild.notifications"), + function (this: Guild) { + this.setnotifcation(); + }, + ); + + this.contextmenu.addbutton( + () => I18n.getTranslation("user.editServerProfile"), + function () { + this.member.showEditProfile(); + }, + ); + + Guild.contextmenu.addbutton( + () => I18n.getTranslation("guild.leave"), + function (this: Guild) { this.confirmleave(); }, null, - function(_){ + function (_) { return this.properties.owner_id !== this.member.user.id; - } + }, ); Guild.contextmenu.addbutton( - ()=>I18n.getTranslation("guild.delete"), - function(this: Guild){ + () => I18n.getTranslation("guild.delete"), + function (this: Guild) { this.confirmDelete(); }, null, - function(_){ + function (_) { return this.properties.owner_id === this.member.user.id; - } + }, ); Guild.contextmenu.addbutton( - ()=>I18n.getTranslation("guild.makeInvite"), - function(this: Guild){ - const d=new Dialog(""); + () => I18n.getTranslation("guild.makeInvite"), + function (this: Guild) { + const d = new Dialog(""); this.makeInviteMenu(d.options); d.show(); }, null, - _=>true, - function(){ + (_) => true, + function () { return this.member.hasPermission("CREATE_INSTANT_INVITE"); - } + }, + ); + Guild.contextmenu.addbutton( + () => I18n.getTranslation("guild.settings"), + function (this: Guild) { + this.generateSettings(); + }, + null, + function () { + return this.member.hasPermission("MANAGE_GUILD"); + }, ); - Guild.contextmenu.addbutton(()=>I18n.getTranslation("guild.settings"), function(this: Guild){ - this.generateSettings(); - },null,function(){ - return this.member.hasPermission("MANAGE_GUILD"); - }); /* -----things left for later----- guild.contextmenu.addbutton("Leave Guild",function(){ console.log(this) @@ -102,162 +123,178 @@ class Guild extends SnowFlake{ },null,_=>{return thisuser.isAdmin()}) */ } - generateSettings(){ - const settings = new Settings(I18n.getTranslation("guild.settingsFor",this.properties.name)); - const textChannels=this.channels.filter(e=>{ + generateSettings() { + const settings = new Settings(I18n.getTranslation("guild.settingsFor", this.properties.name)); + const textChannels = this.channels.filter((e) => { //TODO there are almost certainly more types. is Voice valid? - return new Set([0,5]).has(e.type); + return new Set([0, 5]).has(e.type); }); { const overview = settings.addButton(I18n.getTranslation("guild.overview")); - const form = overview.addForm("", _=>{}, { + const form = overview.addForm("", (_) => {}, { headers: this.headers, traditionalSubmit: true, fetchURL: this.info.api + "/guilds/" + this.id, method: "PATCH", }); - form.addTextInput(I18n.getTranslation("guild.name:"), "name", { initText: this.properties.name }); + form.addTextInput(I18n.getTranslation("guild.name:"), "name", { + initText: this.properties.name, + }); form.addMDInput(I18n.getTranslation("guild.description:"), "description", { initText: this.properties.description, }); - form.addFileInput(I18n.getTranslation("guild.banner:"), "banner", { clear: true }); - form.addFileInput(I18n.getTranslation("guild.icon:"), "icon", { clear: true }); + form.addFileInput(I18n.getTranslation("guild.banner:"), "banner", {clear: true}); + form.addFileInput(I18n.getTranslation("guild.icon:"), "icon", {clear: true}); form.addHR(); - const sysmap=[null,...textChannels.map(e=>e.id)]; - form.addSelect(I18n.getTranslation("guild.systemSelect:"), "system_channel_id", - ["No system messages",...textChannels.map(e=>e.name)],{defaultIndex:sysmap.indexOf(this.properties.system_channel_id)} - ,sysmap); + const sysmap = [null, ...textChannels.map((e) => e.id)]; + form.addSelect( + I18n.getTranslation("guild.systemSelect:"), + "system_channel_id", + ["No system messages", ...textChannels.map((e) => e.name)], + {defaultIndex: sysmap.indexOf(this.properties.system_channel_id)}, + sysmap, + ); - form.addCheckboxInput(I18n.getTranslation("guild.sendrandomwelcome?"),"s1",{ - initState:!(this.properties.system_channel_flags&1) + form.addCheckboxInput(I18n.getTranslation("guild.sendrandomwelcome?"), "s1", { + initState: !(this.properties.system_channel_flags & 1), }); - form.addCheckboxInput(I18n.getTranslation("guild.stickWelcomeReact?"),"s4",{ - initState:!(this.properties.system_channel_flags&8) + form.addCheckboxInput(I18n.getTranslation("guild.stickWelcomeReact?"), "s4", { + initState: !(this.properties.system_channel_flags & 8), }); - form.addCheckboxInput(I18n.getTranslation("guild.boostMessage?"),"s2",{ - initState:!(this.properties.system_channel_flags&2) + form.addCheckboxInput(I18n.getTranslation("guild.boostMessage?"), "s2", { + initState: !(this.properties.system_channel_flags & 2), }); - form.addCheckboxInput(I18n.getTranslation("guild.helpTips?"),"s3",{ - initState:!(this.properties.system_channel_flags&4) + form.addCheckboxInput(I18n.getTranslation("guild.helpTips?"), "s3", { + initState: !(this.properties.system_channel_flags & 4), }); - form.addPreprocessor((e:any)=>{ - let bits=0; - bits+=(1-e.s1)*1; + form.addPreprocessor((e: any) => { + let bits = 0; + bits += (1 - e.s1) * 1; delete e.s1; - bits+=(1-e.s2)*2; + bits += (1 - e.s2) * 2; delete e.s2; - bits+=(1-e.s3)*4; + bits += (1 - e.s3) * 4; delete e.s3; - bits+= (1-e.s4)*8; + bits += (1 - e.s4) * 8; delete e.s4; - e.system_channel_flags=bits; - }) + e.system_channel_flags = bits; + }); form.addHR(); - form.addSelect(I18n.getTranslation("guild.defaultNoti"),"default_message_notifications", - [I18n.getTranslation("guild.onlyMentions"),I18n.getTranslation("guild.all")], - { - defaultIndex:[1,0].indexOf(this.properties.default_message_notifications), - radio:true - },[1,0]); + form.addSelect( + I18n.getTranslation("guild.defaultNoti"), + "default_message_notifications", + [I18n.getTranslation("guild.onlyMentions"), I18n.getTranslation("guild.all")], + { + defaultIndex: [1, 0].indexOf(this.properties.default_message_notifications), + radio: true, + }, + [1, 0], + ); form.addHR(); let region = this.properties.region; - if(!region){ + if (!region) { region = ""; } - form.addTextInput(I18n.getTranslation("guild.region:"), "region", { initText: region }); + form.addTextInput(I18n.getTranslation("guild.region:"), "region", {initText: region}); } - this.makeInviteMenu(settings.addButton(I18n.getTranslation("invite.inviteMaker")),textChannels); + this.makeInviteMenu( + settings.addButton(I18n.getTranslation("invite.inviteMaker")), + textChannels, + ); const s1 = settings.addButton(I18n.getTranslation("guild.roles")); const permlist: [Role, Permissions][] = []; - for(const thing of this.roles){ + for (const thing of this.roles) { permlist.push([thing, thing.permissions]); } - s1.options.push( - new RoleList(permlist, this, this.updateRolePermissions.bind(this),false) - ); + s1.options.push(new RoleList(permlist, this, this.updateRolePermissions.bind(this), false)); { - const emoji=settings.addButton("Emojis"); - emoji.addButtonInput("","Upload Emoji",()=>{ - const popup=new Dialog("Upload emoji"); - const form=popup.options.addForm("",()=>{ - popup.hide(); - },{ - fetchURL:`${this.info.api}/guilds/${this.id}/emojis`, - method:"POST", - headers:this.headers - }); - form.addFileInput("Image:","image",{required:true}); - form.addTextInput("Name:","name",{required:true}); + const emoji = settings.addButton("Emojis"); + emoji.addButtonInput("", "Upload Emoji", () => { + const popup = new Dialog("Upload emoji"); + const form = popup.options.addForm( + "", + () => { + popup.hide(); + }, + { + fetchURL: `${this.info.api}/guilds/${this.id}/emojis`, + method: "POST", + headers: this.headers, + }, + ); + form.addFileInput("Image:", "image", {required: true}); + form.addTextInput("Name:", "name", {required: true}); popup.show(); }); - const containdiv=document.createElement("div"); - const genDiv=()=>{ - containdiv.innerHTML=""; - for(const emoji of this.emojis){ - const div=document.createElement("div"); - div.classList.add("flexltr","emojiOption"); - const emojic=new Emoji(emoji,this); + const containdiv = document.createElement("div"); + const genDiv = () => { + containdiv.innerHTML = ""; + for (const emoji of this.emojis) { + const div = document.createElement("div"); + div.classList.add("flexltr", "emojiOption"); + const emojic = new Emoji(emoji, this); - const text=document.createElement("input"); - text.type="text"; - text.value=emoji.name; - text.addEventListener("change",()=>{ - fetch(`${this.info.api}/guilds/${this.id}/emojis/${emoji.id}`,{ - method:"PATCH", - headers:this.headers, - body:JSON.stringify({name:text.value}) - }).then(e=>{if(!e.ok)text.value=emoji.name;})//if not ok, undo + const text = document.createElement("input"); + text.type = "text"; + text.value = emoji.name; + text.addEventListener("change", () => { + fetch(`${this.info.api}/guilds/${this.id}/emojis/${emoji.id}`, { + method: "PATCH", + headers: this.headers, + body: JSON.stringify({name: text.value}), + }).then((e) => { + if (!e.ok) text.value = emoji.name; + }); //if not ok, undo }); - const del=document.createElement("span"); - del.classList.add("svgicon", "svg-x","deleteEmoji"); - del.onclick=()=>{ - const diaolog=new Dialog(""); + const del = document.createElement("span"); + del.classList.add("svgicon", "svg-x", "deleteEmoji"); + del.onclick = () => { + const diaolog = new Dialog(""); diaolog.options.addTitle("Are you sure you want to delete this emoji?"); - const options=diaolog.options.addOptions("",{ltr:true}); - options.addButtonInput("",I18n.getTranslation("yes"),()=>{ - fetch(`${this.info.api}/guilds/${this.id}/emojis/${emoji.id}`,{ - method:"DELETE", - headers:this.headers - }) + const options = diaolog.options.addOptions("", {ltr: true}); + options.addButtonInput("", I18n.getTranslation("yes"), () => { + fetch(`${this.info.api}/guilds/${this.id}/emojis/${emoji.id}`, { + method: "DELETE", + headers: this.headers, + }); diaolog.hide(); }); - options.addButtonInput("",I18n.getTranslation("no"),()=>{ + options.addButtonInput("", I18n.getTranslation("no"), () => { diaolog.hide(); - }) + }); diaolog.show(); - } + }; - - div.append(emojic.getHTML(true),":",text,":",del); + div.append(emojic.getHTML(true), ":", text, ":", del); containdiv.append(div); } - } - this.onEmojiUpdate=()=>{ - if(!document.body.contains(containdiv)){ - this.onEmojiUpdate=()=>{}; + }; + this.onEmojiUpdate = () => { + if (!document.body.contains(containdiv)) { + this.onEmojiUpdate = () => {}; return; } genDiv(); - } + }; genDiv(); emoji.addHTMLArea(containdiv); } settings.show(); } - makeInviteMenu(options:Options,valid:void|(Channel[])){ - if(!valid){ - valid=this.channels.filter(e=>{ + makeInviteMenu(options: Options, valid: void | Channel[]) { + if (!valid) { + valid = this.channels.filter((e) => { //TODO there are almost certainly more types. is Voice valid? - return new Set([0,5]).has(e.type); + return new Set([0, 5]).has(e.type); }); } - let channel=valid[0]; + let channel = valid[0]; const div = document.createElement("div"); div.classList.add("invitediv"); const text = document.createElement("span"); @@ -270,13 +307,13 @@ class Guild extends SnowFlake{ const copy = document.createElement("span"); copy.classList.add("copybutton", "svgicon", "svg-copy"); copycontainer.append(copy); - copycontainer.onclick = _=>{ - if(text.textContent){ + copycontainer.onclick = (_) => { + if (text.textContent) { navigator.clipboard.writeText(text.textContent); } }; div.append(copycontainer); - const update = ()=>{ + const update = () => { fetch(`${this.info.api}/channels/${channel.id}/invites`, { method: "POST", headers: this.headers, @@ -286,11 +323,11 @@ class Guild extends SnowFlake{ target_user_id: null, max_age: expires + "", max_uses: uses, - temporary: uses !== 0 + temporary: uses !== 0, }), }) - .then(_=>_.json()) - .then(json=>{ + .then((_) => _.json()) + .then((json) => { const params = new URLSearchParams(""); params.set("instance", this.info.wellknown); const encoded = params.toString(); @@ -299,91 +336,102 @@ class Guild extends SnowFlake{ }; options.addTitle(I18n.getTranslation("inviteOptions.title")); - const text2=options.addText(""); - options.addSelect(I18n.getTranslation("invite.channel:"),()=>{},valid.map(e=>e.name)) - .watchForChange((e)=>{ - channel=valid[e]; - text2.setText(I18n.getTranslation("invite.subtext",channel.name,this.properties.name)); - }) + const text2 = options.addText(""); + options + .addSelect( + I18n.getTranslation("invite.channel:"), + () => {}, + valid.map((e) => e.name), + ) + .watchForChange((e) => { + channel = valid[e]; + text2.setText(I18n.getTranslation("invite.subtext", channel.name, this.properties.name)); + }); + options.addSelect( + I18n.getTranslation("invite.expireAfter"), + () => {}, + ["30m", "1h", "6h", "12h", "1d", "7d", "30d", "never"].map((e) => + I18n.getTranslation("inviteOptions." + e), + ), + ).onchange = (e) => { + expires = [1800, 3600, 21600, 43200, 86400, 604800, 2592000, 0][e]; + }; - options.addSelect(I18n.getTranslation("invite.expireAfter"),()=>{}, - ["30m","1h","6h","12h","1d","7d","30d","never"].map((e)=>I18n.getTranslation("inviteOptions."+e)) - ).onchange=(e)=>{expires=[1800, 3600, 21600, 43200, 86400, 604800, 2592000, 0][e];}; + const timeOptions = ["1", "5", "10", "25", "50", "100"].map((e) => + I18n.getTranslation("inviteOptions.limit", e), + ); + timeOptions.unshift(I18n.getTranslation("inviteOptions.noLimit")); + options.addSelect(I18n.getTranslation("invite.expireAfter"), () => {}, timeOptions).onchange = ( + e, + ) => { + uses = [0, 1, 5, 10, 25, 50, 100][e]; + }; - const timeOptions=["1","5","10","25","50","100"].map((e)=>I18n.getTranslation("inviteOptions.limit",e)) - timeOptions.unshift(I18n.getTranslation("inviteOptions.noLimit")) - options.addSelect(I18n.getTranslation("invite.expireAfter"),()=>{},timeOptions) - .onchange=(e)=>{uses=[0, 1, 5, 10, 25, 50, 100][e];}; - - options.addButtonInput("",I18n.getTranslation("invite.createInvite"),()=>{ + options.addButtonInput("", I18n.getTranslation("invite.createInvite"), () => { update(); - }) + }); options.addHTMLArea(div); } - roleUpdate:(role:Role,added:-1|0|1)=>unknown=()=>{}; - sortRoles(){ - this.roles.sort((a,b)=>(b.position-a.position)); + roleUpdate: (role: Role, added: -1 | 0 | 1) => unknown = () => {}; + sortRoles() { + this.roles.sort((a, b) => b.position - a.position); } - async recalcRoles(){ - let position=this.roles.length; - const map=this.roles.map(_=>{ + async recalcRoles() { + let position = this.roles.length; + const map = this.roles.map((_) => { position--; - return {id:_.id,position}; - }) - await fetch(this.info.api+"/guilds/"+this.id+"/roles",{ - method:"PATCH", - body:JSON.stringify(map), - headers:this.headers - }) + return {id: _.id, position}; + }); + await fetch(this.info.api + "/guilds/" + this.id + "/roles", { + method: "PATCH", + body: JSON.stringify(map), + headers: this.headers, + }); } - newRole(rolej:rolesjson){ - const role=new Role(rolej,this); + newRole(rolej: rolesjson) { + const role = new Role(rolej, this); this.roles.push(role); this.roleids.set(role.id, role); this.sortRoles(); - this.roleUpdate(role,1); + this.roleUpdate(role, 1); } - updateRole(rolej:rolesjson){ - const role=this.roleids.get(rolej.id) as Role; + updateRole(rolej: rolesjson) { + const role = this.roleids.get(rolej.id) as Role; role.newJson(rolej); - this.roleUpdate(role,0); + this.roleUpdate(role, 0); } - memberupdate(json:memberjson){ - let member:undefined|Member=undefined; - for(const thing of this.members){ - if(thing.id===json.id){ - member=thing; + memberupdate(json: memberjson) { + let member: undefined | Member = undefined; + for (const thing of this.members) { + if (thing.id === json.id) { + member = thing; break; } } - if(!member) return; + if (!member) return; member.update(json); - if(member===this.member){ + if (member === this.member) { console.log(member); this.loadGuild(); } } - deleteRole(id:string){ + deleteRole(id: string) { const role = this.roleids.get(id); - if(!role) return; + if (!role) return; this.roleids.delete(id); - this.roles.splice(this.roles.indexOf(role),1); - this.roleUpdate(role,-1); + this.roles.splice(this.roles.indexOf(role), 1); + this.roleUpdate(role, -1); } - onEmojiUpdate=(_:emojipjson[])=>{}; - constructor( - json: guildjson | -1, - owner: Localuser, - member: memberjson | User | null - ){ - if(json === -1 || member === null){ + onEmojiUpdate = (_: emojipjson[]) => {}; + constructor(json: guildjson | -1, owner: Localuser, member: memberjson | User | null) { + if (json === -1 || member === null) { super("@me"); return; } - if(json.stickers.length){ + if (json.stickers.length) { console.log(json.stickers, ":3"); } super(json.id); @@ -398,52 +446,57 @@ class Guild extends SnowFlake{ this.roleids = new Map(); this.message_notifications = 0; - for(const roley of json.roles){ + for (const roley of json.roles) { const roleh = new Role(roley, this); this.roles.push(roleh); this.roleids.set(roleh.id, roleh); } this.sortRoles(); - if(member instanceof User){ + if (member instanceof User) { console.warn(member); - Member.resolveMember(member, this).then(_=>{ - if(_){ + Member.resolveMember(member, this).then((_) => { + if (_) { this.member = _; - }else{ + } else { console.error("Member was unable to resolve"); } }); - }else{ - Member.new(member, this).then(_=>{ - if(_){ + } else { + Member.new(member, this).then((_) => { + if (_) { this.member = _; } }); } - this.perminfo ??= { channels: {} }; - for(const thing of json.channels){ + this.perminfo ??= {channels: {}}; + for (const thing of json.channels) { const temp = new Channel(thing, this); this.channels.push(temp); this.localuser.channelids.set(temp.id, temp); } this.headchannels = []; - for(const thing of this.channels){ + for (const thing of this.channels) { const parent = thing.resolveparent(this); - if(!parent){ + if (!parent) { this.headchannels.push(thing); } } this.prevchannel = this.localuser.channelids.get(this.perminfo.prevchannel); } - get perminfo(){ + get perminfo() { return this.localuser.perminfo.guilds[this.id]; } - set perminfo(e){ + set perminfo(e) { this.localuser.perminfo.guilds[this.id] = e; } notisetting(settings: { - channel_overrides: {message_notifications: number,muted: boolean,mute_config: {selected_time_window: number,end_time: number},channel_id: string}[]; + channel_overrides: { + message_notifications: number; + muted: boolean; + mute_config: {selected_time_window: number; end_time: number}; + channel_id: string; + }[]; message_notifications: any; flags?: number; hide_muted_channels?: boolean; @@ -456,103 +509,112 @@ class Guild extends SnowFlake{ suppress_roles?: boolean; version?: number; guild_id?: string; - }){ + }) { this.message_notifications = settings.message_notifications; - for(const override of settings.channel_overrides){ - const channel=this.localuser.channelids.get(override.channel_id); - if(!channel) continue; + for (const override of settings.channel_overrides) { + const channel = this.localuser.channelids.get(override.channel_id); + if (!channel) continue; channel.handleUserOverrides(override); } } - setnotifcation(){ - - const options=["all", "onlyMentions", "none"].map(e=>I18n.getTranslation("guild."+e)); - const notiselect=new Dialog(""); - const form=notiselect.options.addForm("",(_,sent:any)=>{ - notiselect.hide(); - this.message_notifications = sent.message_notifications; - },{ - fetchURL:`${this.info.api}/users/@me/guilds/${this.id}/settings/`, - method:"PATCH", - headers:this.headers - }); - form.addSelect(I18n.getTranslation("guild.selectnoti"),"message_notifications",options,{ - radio:true, - defaultIndex:this.message_notifications - },[0,1,2]); + setnotifcation() { + const options = ["all", "onlyMentions", "none"].map((e) => I18n.getTranslation("guild." + e)); + const notiselect = new Dialog(""); + const form = notiselect.options.addForm( + "", + (_, sent: any) => { + notiselect.hide(); + this.message_notifications = sent.message_notifications; + }, + { + fetchURL: `${this.info.api}/users/@me/guilds/${this.id}/settings/`, + method: "PATCH", + headers: this.headers, + }, + ); + form.addSelect( + I18n.getTranslation("guild.selectnoti"), + "message_notifications", + options, + { + radio: true, + defaultIndex: this.message_notifications, + }, + [0, 1, 2], + ); notiselect.show(); } - confirmleave(){ + confirmleave() { const full = new Dialog(""); - full.options.addTitle(I18n.getTranslation("guild.confirmLeave")) - const options=full.options.addOptions("",{ltr:true}); - options.addButtonInput("",I18n.getTranslation("guild.yesLeave"),()=>{ - this.leave().then(_=>{ + full.options.addTitle(I18n.getTranslation("guild.confirmLeave")); + const options = full.options.addOptions("", {ltr: true}); + options.addButtonInput("", I18n.getTranslation("guild.yesLeave"), () => { + this.leave().then((_) => { full.hide(); }); }); - options.addButtonInput("",I18n.getTranslation("guild.noLeave"),()=>{ + options.addButtonInput("", I18n.getTranslation("guild.noLeave"), () => { full.hide(); }); full.show(); } - async leave(){ + async leave() { return fetch(this.info.api + "/users/@me/guilds/" + this.id, { method: "DELETE", headers: this.headers, }); } - printServers(){ + printServers() { let build = ""; - for(const thing of this.headchannels){ + for (const thing of this.headchannels) { build += thing.name + ":" + thing.position + "\n"; - for(const thingy of thing.children){ + for (const thingy of thing.children) { build += " " + thingy.name + ":" + thingy.position + "\n"; } } console.log(build); } - calculateReorder(){ + calculateReorder() { let position = -1; const build: { id: string; position: number | undefined; parent_id: string | undefined; }[] = []; - for(const thing of this.headchannels){ + for (const thing of this.headchannels) { const thisthing: { - id: string; - position: number | undefined; - parent_id: string | undefined; - } = { id: thing.id, position: undefined, parent_id: undefined }; - if(thing.position <= position){ + id: string; + position: number | undefined; + parent_id: string | undefined; + } = {id: thing.id, position: undefined, parent_id: undefined}; + if (thing.position <= position) { thing.position = thisthing.position = position + 1; } position = thing.position; console.log(position); - if(thing.move_id && thing.move_id !== thing.parent_id){ + if (thing.move_id && thing.move_id !== thing.parent_id) { thing.parent_id = thing.move_id; thisthing.parent_id = thing.parent?.id; thing.move_id = undefined; } - if(thisthing.position || thisthing.parent_id){ + if (thisthing.position || thisthing.parent_id) { build.push(thisthing); } - if(thing.children.length > 0){ + if (thing.children.length > 0) { const things = thing.calculateReorder(); - for(const thing of things){ + for (const thing of things) { build.push(thing); } } } console.log(build); this.printServers(); - if(build.length === 0){ + if (build.length === 0) { return; } const serverbug = false; - if(serverbug){ - for(const thing of build){ + if (serverbug) { + for (const thing of build) { console.log(build, thing); fetch(this.info.api + "/guilds/" + this.id + "/channels", { method: "PATCH", @@ -560,7 +622,7 @@ class Guild extends SnowFlake{ body: JSON.stringify([thing]), }); } - }else{ + } else { fetch(this.info.api + "/guilds/" + this.id + "/channels", { method: "PATCH", headers: this.headers, @@ -568,174 +630,175 @@ class Guild extends SnowFlake{ }); } } - get localuser(){ + get localuser() { return this.owner; } - get info(){ + get info() { return this.owner.info; } - sortchannels(){ - this.headchannels.sort((a, b)=>{ + sortchannels() { + this.headchannels.sort((a, b) => { return a.position - b.position; }); } - static generateGuildIcon(guild: Guild | (invitejson["guild"] & { info: { cdn: string } })){ + static generateGuildIcon(guild: Guild | (invitejson["guild"] & {info: {cdn: string}})) { const divy = document.createElement("div"); divy.classList.add("servernoti"); const noti = document.createElement("div"); noti.classList.add("unread"); divy.append(noti); - if(guild instanceof Guild){ + if (guild instanceof Guild) { guild.localuser.guildhtml.set(guild.id, divy); - guild.html=divy; + guild.html = divy; } let icon: string | null; - if(guild instanceof Guild){ + if (guild instanceof Guild) { icon = guild.properties.icon; - }else{ + } else { icon = guild.icon; } - if(icon !== null){ + if (icon !== null) { const img = document.createElement("img"); img.classList.add("pfp", "servericon"); img.src = guild.info.cdn + "/icons/" + guild.id + "/" + icon + ".png"; divy.appendChild(img); - if(guild instanceof Guild){ - img.onclick = ()=>{ + if (guild instanceof Guild) { + img.onclick = () => { console.log(guild.loadGuild); guild.loadGuild(); guild.loadChannel(); }; - Guild.contextmenu.bindContextmenu(img, guild,undefined); + Guild.contextmenu.bindContextmenu(img, guild, undefined); } - }else{ + } else { const div = document.createElement("div"); let name: string; - if(guild instanceof Guild){ + if (guild instanceof Guild) { name = guild.properties.name; - }else{ + } else { name = guild.name; } const build = name .replace(/'s /g, " ") - .replace(/\w+/g, word=>word[0]) + .replace(/\w+/g, (word) => word[0]) .replace(/\s/g, ""); div.textContent = build; div.classList.add("blankserver", "servericon"); divy.appendChild(div); - if(guild instanceof Guild){ - div.onclick = ()=>{ + if (guild instanceof Guild) { + div.onclick = () => { guild.loadGuild(); guild.loadChannel(); }; - Guild.contextmenu.bindContextmenu(div, guild,undefined); + Guild.contextmenu.bindContextmenu(div, guild, undefined); } } return divy; } - generateGuildIcon(){ + generateGuildIcon() { return Guild.generateGuildIcon(this); } - confirmDelete(){ + confirmDelete() { let confirmname = ""; const full = new Dialog(""); - full.options.addTitle(I18n.getTranslation("guild.confirmDelete",this.properties.name)); - full.options.addTextInput(I18n.getTranslation("guild.serverName"),()=>{}).onchange=(e)=>confirmname=e; + full.options.addTitle(I18n.getTranslation("guild.confirmDelete", this.properties.name)); + full.options.addTextInput(I18n.getTranslation("guild.serverName"), () => {}).onchange = (e) => + (confirmname = e); - const options=full.options.addOptions("",{ltr:true}); - options.addButtonInput("",I18n.getTranslation("guild.yesDelete"),()=>{ - if(confirmname !== this.properties.name){ + const options = full.options.addOptions("", {ltr: true}); + options.addButtonInput("", I18n.getTranslation("guild.yesDelete"), () => { + if (confirmname !== this.properties.name) { //TODO maybe some sort of form error? idk alert("names don't match"); return; } - this.delete().then(_=>{ + this.delete().then((_) => { full.hide(); }); }); - options.addButtonInput("",I18n.getTranslation("guild.noDelete"),()=>{ + options.addButtonInput("", I18n.getTranslation("guild.noDelete"), () => { full.hide(); }); full.show(); } - async delete(){ + async delete() { return fetch(this.info.api + "/guilds/" + this.id + "/delete", { method: "POST", headers: this.headers, }); } - get mentions(){ - let mentions=0; - for(const thing of this.channels){ - mentions+=thing.mentions; + get mentions() { + let mentions = 0; + for (const thing of this.channels) { + mentions += thing.mentions; } return mentions; } - unreads(html?: HTMLElement | undefined){ - if(html){ + unreads(html?: HTMLElement | undefined) { + if (html) { this.html = html; - }else{ + } else { html = this.html; } - if(!html){ + if (!html) { return; } let read = true; - let mentions=this.mentions; - for(const thing of this.channels){ - if(thing.hasunreads){ + let mentions = this.mentions; + for (const thing of this.channels) { + if (thing.hasunreads) { read = false; break; } } - const noti=html.children[0]; - if(mentions!==0){ + const noti = html.children[0]; + if (mentions !== 0) { noti.classList.add("pinged"); - noti.textContent=""+mentions; - }else{ - noti.textContent=""; + noti.textContent = "" + mentions; + } else { + noti.textContent = ""; noti.classList.remove("pinged"); } - if(read){ + if (read) { noti.classList.remove("notiunread"); - }else{ + } else { noti.classList.add("notiunread"); } } - getHTML(){ + getHTML() { //this.printServers(); this.sortchannels(); this.printServers(); const build = document.createElement("div"); - for(const thing of this.headchannels){ + for (const thing of this.headchannels) { build.appendChild(thing.createguildHTML(this.isAdmin())); } return build; } - isAdmin(){ + isAdmin() { return this.member.isAdmin(); } - async markAsRead(){ + async markAsRead() { const build: { - read_states: { - channel_id: string; - message_id: string | null | undefined; - read_state_type: number; - }[]; - } = { read_states: [] }; - for(const thing of this.channels){ - if(thing.hasunreads){ + read_states: { + channel_id: string; + message_id: string | null | undefined; + read_state_type: number; + }[]; + } = {read_states: []}; + for (const thing of this.channels) { + if (thing.hasunreads) { build.read_states.push({ channel_id: thing.id, message_id: thing.lastmessageid, read_state_type: 0, }); thing.lastreadmessageid = thing.lastmessageid; - if(!thing.myhtml)continue; + if (!thing.myhtml) continue; thing.myhtml.classList.remove("cunread"); } } @@ -746,29 +809,29 @@ class Guild extends SnowFlake{ body: JSON.stringify(build), }); } - hasRole(r: Role | string){ + hasRole(r: Role | string) { console.log("this should run"); - if(r instanceof Role){ + if (r instanceof Role) { r = r.id; } return this.member.hasRole(r); } - loadChannel(ID?: string | undefined| null,addstate=true){ - if(ID){ + loadChannel(ID?: string | undefined | null, addstate = true) { + if (ID) { const channel = this.localuser.channelids.get(ID); - if(channel){ + if (channel) { channel.getHTML(addstate); return; } } - if(this.prevchannel&&ID!==null){ + if (this.prevchannel && ID !== null) { console.log(this.prevchannel); this.prevchannel.getHTML(addstate); return; } - if(this.id!=="@me"){ - for(const thing of this.channels){ - if(thing.type!==4){ + if (this.id !== "@me") { + for (const thing of this.channels) { + if (thing.type !== 4) { thing.getHTML(addstate); return; } @@ -777,14 +840,14 @@ class Guild extends SnowFlake{ this.removePrevChannel(); this.noChannel(addstate); } - removePrevChannel(){ - if(this.localuser.channelfocus){ + removePrevChannel() { + if (this.localuser.channelfocus) { this.localuser.channelfocus.infinite.delete(); } - if(this !== this.localuser.lookingguild){ + if (this !== this.localuser.lookingguild) { this.loadGuild(); } - if(this.localuser.channelfocus && this.localuser.channelfocus.myhtml){ + if (this.localuser.channelfocus && this.localuser.channelfocus.myhtml) { this.localuser.channelfocus.myhtml.classList.remove("viewChannel"); } this.prevchannel = undefined; @@ -793,14 +856,14 @@ class Guild extends SnowFlake{ 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=""; + (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){ - if(addstate){ - history.pushState([this.id,undefined], "", "/channels/" + this.id); + noChannel(addstate: boolean) { + if (addstate) { + history.pushState([this.id, undefined], "", "/channels/" + this.id); } this.localuser.pageTitle(I18n.getTranslation("guild.emptytitle")); const channelTopic = document.getElementById("channelTopic") as HTMLSpanElement; @@ -811,82 +874,90 @@ class Guild extends SnowFlake{ this.localuser.getSidePannel(); const messages = document.getElementById("channelw") as HTMLDivElement; - for(const thing of Array.from(messages.getElementsByClassName("messagecontainer"))){ + for (const thing of Array.from(messages.getElementsByClassName("messagecontainer"))) { thing.remove(); } - const h1=document.createElement("h1"); - h1.classList.add("messagecontainer") - h1.textContent=I18n.getTranslation("guild.emptytext"); + const h1 = document.createElement("h1"); + h1.classList.add("messagecontainer"); + h1.textContent = I18n.getTranslation("guild.emptytext"); messages.append(h1); } - loadGuild(){ + loadGuild() { this.localuser.loadGuild(this.id); } - updateChannel(json: channeljson){ + updateChannel(json: channeljson) { const channel = this.localuser.channelids.get(json.id); - if(channel){ + if (channel) { channel.updateChannel(json); this.headchannels = []; - for(const thing of this.channels){ + for (const thing of this.channels) { thing.children = []; } this.headchannels = []; - for(const thing of this.channels){ + for (const thing of this.channels) { const parent = thing.resolveparent(this); - if(!parent){ + if (!parent) { this.headchannels.push(thing); } } this.printServers(); } } - createChannelpac(json: channeljson){ + createChannelpac(json: channeljson) { const thischannel = new Channel(json, this); this.localuser.channelids.set(json.id, thischannel); this.channels.push(thischannel); thischannel.resolveparent(this); - if(!thischannel.parent){ + if (!thischannel.parent) { this.headchannels.push(thischannel); } this.calculateReorder(); this.printServers(); return thischannel; } - createchannels(func = this.createChannel){ - const options=["text", "announcement","voice"].map(e=>I18n.getTranslation("channel."+e)); + createchannels(func = this.createChannel) { + const options = ["text", "announcement", "voice"].map((e) => + I18n.getTranslation("channel." + e), + ); - const channelselect=new Dialog(""); - const form=channelselect.options.addForm("",(e:any)=>{ - func(e.name,e.type); + const channelselect = new Dialog(""); + const form = channelselect.options.addForm("", (e: any) => { + func(e.name, e.type); channelselect.hide(); }); - form.addSelect(I18n.getTranslation("channel.selectType"),"type",options,{radio:true},[0,5,2]); - form.addTextInput(I18n.getTranslation("channel.selectName"),"name"); + form.addSelect( + I18n.getTranslation("channel.selectType"), + "type", + options, + {radio: true}, + [0, 5, 2], + ); + form.addTextInput(I18n.getTranslation("channel.selectName"), "name"); channelselect.show(); } - createcategory(){ + createcategory() { const category = 4; - const channelselect=new Dialog(""); - const options=channelselect.options; - const form=options.addForm("",(e:any)=>{ + const channelselect = new Dialog(""); + const options = channelselect.options; + const form = options.addForm("", (e: any) => { this.createChannel(e.name, category); channelselect.hide(); }); - form.addTextInput(I18n.getTranslation("channel.selectCatName"),"name"); + form.addTextInput(I18n.getTranslation("channel.selectCatName"), "name"); channelselect.show(); } - delChannel(json: channeljson){ + delChannel(json: channeljson) { const channel = this.localuser.channelids.get(json.id); this.localuser.channelids.delete(json.id); - if(!channel)return; + if (!channel) return; this.channels.splice(this.channels.indexOf(channel), 1); const indexy = this.headchannels.indexOf(channel); - if(indexy !== -1){ + if (indexy !== -1) { this.headchannels.splice(indexy, 1); } - if(channel===this.prevchannel){ - this.prevchannel=undefined; + if (channel === this.prevchannel) { + this.prevchannel = undefined; } /* const build=[]; @@ -905,35 +976,32 @@ class Guild extends SnowFlake{ */ this.printServers(); } - createChannel(name: string, type: number){ + createChannel(name: string, type: number) { fetch(this.info.api + "/guilds/" + this.id + "/channels", { method: "POST", headers: this.headers, - body: JSON.stringify({ name, type }), + body: JSON.stringify({name, type}), }); } - async createRole(name: string){ - const fetched = await fetch( - this.info.api + "/guilds/" + this.id + "roles", - { - method: "POST", - headers: this.headers, - body: JSON.stringify({ - name, - color: 0, - permissions: "0", - }), - } - ); + async createRole(name: string) { + const fetched = await fetch(this.info.api + "/guilds/" + this.id + "roles", { + method: "POST", + headers: this.headers, + body: JSON.stringify({ + name, + color: 0, + permissions: "0", + }), + }); const json = await fetched.json(); const role = new Role(json, this); this.roleids.set(role.id, role); this.roles.push(role); return role; } - async updateRolePermissions(id: string, perms: Permissions){ + async updateRolePermissions(id: string, perms: Permissions) { const role = this.roleids.get(id); - if(!role){ + if (!role) { return; } role.permissions.allow = perms.allow; @@ -955,4 +1023,4 @@ class Guild extends SnowFlake{ } } Guild.setupcontextmenu(); -export{ Guild }; +export {Guild}; diff --git a/src/webpage/home.html b/src/webpage/home.html index 3434704..c1aa873 100644 --- a/src/webpage/home.html +++ b/src/webpage/home.html @@ -1,39 +1,44 @@ - + - - - + + Jank Client - - - - - - - + + + + + + + - +
-

Welcome to Jank Client

-

Jank Client is a Spacebar-compatible client seeking to be as good as it can be with many features including:

+

+ Jank Client is a Spacebar-compatible client seeking to be as good as it can be with many + features including: +

  • Direct Messaging
  • Reactions support
  • @@ -47,18 +52,18 @@

Spacebar-Compatible Instances:

-
-
+

Contribute to Jank Client

-

We always appreciate some help, whether that be in the form of bug reports, code, help translate, or even just pointing out some typos.


- - Github - +

+ We always appreciate some help, whether that be in the form of bug reports, code, help + translate, or even just pointing out some typos. +

+
+ Github
- diff --git a/src/webpage/home.ts b/src/webpage/home.ts index 20cac87..7be581b 100644 --- a/src/webpage/home.ts +++ b/src/webpage/home.ts @@ -1,40 +1,56 @@ -import { I18n } from "./i18n.js"; -import{ mobile }from"./utils/utils.js"; +import {I18n} from "./i18n.js"; +import {mobile} from "./utils/utils.js"; console.log(mobile); const serverbox = document.getElementById("instancebox") as HTMLDivElement; -(async ()=>{ +(async () => { await I18n.done; - const openClient=document.getElementById("openClient") - const welcomeJank=document.getElementById("welcomeJank") - const box1title=document.getElementById("box1title") - const box1Items=document.getElementById("box1Items") - const compatableInstances=document.getElementById("compatableInstances") - const box3title=document.getElementById("box3title") - const box3description=document.getElementById("box3description") - if(openClient&&welcomeJank&&compatableInstances&&box3title&&box3description&&box1title&&box1Items){ - openClient.textContent=I18n.getTranslation("htmlPages.openClient"); - welcomeJank.textContent=I18n.getTranslation("htmlPages.welcomeJank"); - box1title.textContent=I18n.getTranslation("htmlPages.box1title"); + const openClient = document.getElementById("openClient"); + const welcomeJank = document.getElementById("welcomeJank"); + const box1title = document.getElementById("box1title"); + const box1Items = document.getElementById("box1Items"); + const compatableInstances = document.getElementById("compatableInstances"); + const box3title = document.getElementById("box3title"); + const box3description = document.getElementById("box3description"); + if ( + openClient && + welcomeJank && + compatableInstances && + box3title && + box3description && + box1title && + box1Items + ) { + openClient.textContent = I18n.getTranslation("htmlPages.openClient"); + welcomeJank.textContent = I18n.getTranslation("htmlPages.welcomeJank"); + box1title.textContent = I18n.getTranslation("htmlPages.box1title"); - compatableInstances.textContent=I18n.getTranslation("htmlPages.compatableInstances"); - box3title.textContent=I18n.getTranslation("htmlPages.box3title"); - box3description.textContent=I18n.getTranslation("htmlPages.box3description"); + compatableInstances.textContent = I18n.getTranslation("htmlPages.compatableInstances"); + box3title.textContent = I18n.getTranslation("htmlPages.box3title"); + box3description.textContent = I18n.getTranslation("htmlPages.box3description"); - const items=I18n.getTranslation("htmlPages.box1Items").split("|"); - let i=0; + const items = I18n.getTranslation("htmlPages.box1Items").split("|"); + let i = 0; //@ts-ignore ts is being dumb here - for(const item of box1Items.children){ - (item as HTMLElement).textContent=items[i]; + for (const item of box1Items.children) { + (item as HTMLElement).textContent = items[i]; i++; } - }else{ - console.error(openClient,welcomeJank,compatableInstances,box3title,box3description,box1title,box1Items) + } else { + console.error( + openClient, + welcomeJank, + compatableInstances, + box3title, + box3description, + box1title, + box1Items, + ); } -})() +})(); fetch("/instances.json") - .then(_=>_.json()) + .then((_) => _.json()) .then( async ( json: { @@ -45,76 +61,77 @@ fetch("/instances.json") url?: string; display?: boolean; online?: boolean; - uptime: { alltime: number; daytime: number; weektime: number }; + uptime: {alltime: number; daytime: number; weektime: number}; urls: { - wellknown: string; - api: string; - cdn: string; - gateway: string; - login?: string; + wellknown: string; + api: string; + cdn: string; + gateway: string; + login?: string; }; - }[] - )=>{ + }[], + ) => { await I18n.done; console.warn(json); - for(const instance of json){ - if(instance.display === false){ + for (const instance of json) { + if (instance.display === false) { continue; } const div = document.createElement("div"); div.classList.add("flexltr", "instance"); - if(instance.image){ + if (instance.image) { const img = document.createElement("img"); img.src = instance.image; div.append(img); } const statbox = document.createElement("div"); - statbox.classList.add("flexttb","flexgrow"); + statbox.classList.add("flexttb", "flexgrow"); { const textbox = document.createElement("div"); textbox.classList.add("flexttb", "instancetextbox"); const title = document.createElement("h2"); title.innerText = instance.name; - if(instance.online !== undefined){ + if (instance.online !== undefined) { const status = document.createElement("span"); status.innerText = instance.online ? "Online" : "Offline"; status.classList.add("instanceStatus"); title.append(status); } textbox.append(title); - if(instance.description || instance.descriptionLong){ + if (instance.description || instance.descriptionLong) { const p = document.createElement("p"); - if(instance.descriptionLong){ + if (instance.descriptionLong) { p.innerText = instance.descriptionLong; - }else if(instance.description){ + } else if (instance.description) { p.innerText = instance.description; } textbox.append(p); } statbox.append(textbox); } - if(instance.uptime){ + if (instance.uptime) { const stats = document.createElement("div"); stats.classList.add("flexltr"); const span = document.createElement("span"); - span.innerText = I18n.getTranslation("home.uptimeStats",Math.round( - instance.uptime.alltime * 100 - )+"",Math.round( - instance.uptime.weektime * 100 - )+"",Math.round(instance.uptime.daytime * 100)+"") + span.innerText = I18n.getTranslation( + "home.uptimeStats", + Math.round(instance.uptime.alltime * 100) + "", + Math.round(instance.uptime.weektime * 100) + "", + Math.round(instance.uptime.daytime * 100) + "", + ); stats.append(span); statbox.append(stats); } div.append(statbox); - div.onclick = _=>{ - if(instance.online){ + div.onclick = (_) => { + if (instance.online) { window.location.href = "/register.html?instance=" + encodeURI(instance.name); - }else{ + } else { alert(I18n.getTranslation("home.warnOffiline")); } }; serverbox.append(div); } - } + }, ); diff --git a/src/webpage/hover.ts b/src/webpage/hover.ts index aa02bbd..30f53a6 100644 --- a/src/webpage/hover.ts +++ b/src/webpage/hover.ts @@ -1,57 +1,58 @@ -import { Contextmenu } from "./contextmenu.js"; -import { MarkDown } from "./markdown.js"; +import {Contextmenu} from "./contextmenu.js"; +import {MarkDown} from "./markdown.js"; -class Hover{ - str:string|MarkDown|(()=>Promise|MarkDown|string); - constructor(txt:string|MarkDown|(()=>Promise|MarkDown|string)){ - this.str=txt; - } - addEvent(elm:HTMLElement){ - let timeOut=setTimeout(()=>{},0); - let elm2=document.createElement("div"); - elm.addEventListener("mouseover",()=>{ - timeOut=setTimeout(async ()=>{ - elm2=await this.makeHover(elm); - },750) - }); - elm.addEventListener("mouseout",()=>{ - clearTimeout(timeOut); - elm2.remove(); - }); - new MutationObserver(function (e) { - if (e[0].removedNodes) { - clearTimeout(timeOut); - elm2.remove(); - }; - }).observe(elm,{ childList: true }); - } - async makeHover(elm:HTMLElement){ - if(!document.contains(elm)) return document.createDocumentFragment() as unknown as HTMLDivElement; - const div=document.createElement("div"); - if(this.str instanceof MarkDown){ - div.append(this.str.makeHTML()) - }else if(this.str instanceof Function){ - const hover=await this.str(); - if(hover instanceof MarkDown){ - div.append(hover.makeHTML()); - }else{ - div.innerText=hover; - } - }else{ - div.innerText=this.str; - } - const box=elm.getBoundingClientRect(); - div.style.top=(box.bottom+4)+"px"; - div.style.left=Math.floor(box.left+box.width/2)+"px"; - div.classList.add("hoverthing"); - div.style.opacity="0"; - setTimeout(()=>{ - div.style.opacity="1"; - },10) - document.body.append(div); - Contextmenu.keepOnScreen(div); - console.log(div,elm); - return div; - } +class Hover { + str: string | MarkDown | (() => Promise | MarkDown | string); + constructor(txt: string | MarkDown | (() => Promise | MarkDown | string)) { + this.str = txt; + } + addEvent(elm: HTMLElement) { + let timeOut = setTimeout(() => {}, 0); + let elm2 = document.createElement("div"); + elm.addEventListener("mouseover", () => { + timeOut = setTimeout(async () => { + elm2 = await this.makeHover(elm); + }, 750); + }); + elm.addEventListener("mouseout", () => { + clearTimeout(timeOut); + elm2.remove(); + }); + new MutationObserver(function (e) { + if (e[0].removedNodes) { + clearTimeout(timeOut); + elm2.remove(); + } + }).observe(elm, {childList: true}); + } + async makeHover(elm: HTMLElement) { + if (!document.contains(elm)) + return document.createDocumentFragment() as unknown as HTMLDivElement; + const div = document.createElement("div"); + if (this.str instanceof MarkDown) { + div.append(this.str.makeHTML()); + } else if (this.str instanceof Function) { + const hover = await this.str(); + if (hover instanceof MarkDown) { + div.append(hover.makeHTML()); + } else { + div.innerText = hover; + } + } else { + div.innerText = this.str; + } + const box = elm.getBoundingClientRect(); + div.style.top = box.bottom + 4 + "px"; + div.style.left = Math.floor(box.left + box.width / 2) + "px"; + div.classList.add("hoverthing"); + div.style.opacity = "0"; + setTimeout(() => { + div.style.opacity = "1"; + }, 10); + document.body.append(div); + Contextmenu.keepOnScreen(div); + console.log(div, elm); + return div; + } } -export{Hover} +export {Hover}; diff --git a/src/webpage/i18n.ts b/src/webpage/i18n.ts index 742ba32..8ae5f20 100644 --- a/src/webpage/i18n.ts +++ b/src/webpage/i18n.ts @@ -1,122 +1,118 @@ //@ts-ignore import {langs} from "./translations/langs.js"; -const langmap=new Map(); -for(const lang of Object.keys(langs) as string[]){ - langmap.set(lang,langs[lang]); +const langmap = new Map(); +for (const lang of Object.keys(langs) as string[]) { + langmap.set(lang, langs[lang]); } console.log(langs); -type translation={ - [key:string]:string|translation +type translation = { + [key: string]: string | translation; }; -let res:()=>unknown=()=>{}; -class I18n{ - static lang:string; - static translations:translation[]=[]; - static done=new Promise((res2,_reject)=>{ - res=res2; - }); - static async create(lang:string){ +let res: () => unknown = () => {}; +class I18n { + static lang: string; + static translations: translation[] = []; + static done = new Promise((res2, _reject) => { + res = res2; + }); + static async create(lang: string) { + const json = (await (await fetch("/translations/" + lang + ".json")).json()) as translation; + const translations: translation[] = []; + translations.push(json); + if (lang !== "en") { + translations.push((await (await fetch("/translations/en.json")).json()) as translation); + } + this.lang = lang; + this.translations = translations; - const json=await (await fetch("/translations/"+lang+".json")).json() as translation; - const translations:translation[]=[]; - translations.push(json); - if(lang!=="en"){ - translations.push(await (await fetch("/translations/en.json")).json() as translation); - } - this.lang=lang; - this.translations=translations; + res(); + } + static getTranslation(msg: string, ...params: string[]): string { + let str: string | undefined; + const path = msg.split("."); + for (const json of this.translations) { + let jsont: string | translation = json; + for (const thing of path) { + if (typeof jsont !== "string" && jsont !== undefined) { + jsont = jsont[thing]; + } else { + jsont = json; + break; + } + } - res(); - } - static getTranslation(msg:string,...params:string[]):string{ - let str:string|undefined; - const path=msg.split("."); - for(const json of this.translations){ - let jsont:string|translation=json; - for(const thing of path){ - if(typeof jsont !== "string" && jsont!==undefined){ - jsont=jsont[thing]; + if (typeof jsont === "string") { + str = jsont; + break; + } + } + if (str) { + return this.fillInBlanks(str, params); + } else { + throw new Error(msg + " not found"); + } + } + static fillInBlanks(msg: string, params: string[]): string { + //thanks to geotale for the regex + msg = msg.replace(/\$\d+/g, (match) => { + const number = Number(match.slice(1)); + if (params[number - 1]) { + return params[number - 1]; + } else { + return match; + } + }); + msg = msg.replace(/{{(.+?)}}/g, (str, match: string) => { + const [op, strsSplit] = this.fillInBlanks(match, params).split(":"); + const [first, ...strs] = strsSplit.split("|"); + switch (op.toUpperCase()) { + case "PLURAL": { + const numb = Number(first); + if (numb === 0) { + return strs[strs.length - 1]; + } + return strs[Math.min(strs.length - 1, numb - 1)]; + } + case "GENDER": { + if (first === "male") { + return strs[0]; + } else if (first === "female") { + return strs[1]; + } else if (first === "neutral") { + if (strs[2]) { + return strs[2]; + } else { + return strs[0]; + } + } + } + } + return str; + }); - }else{ - jsont=json; - break; - } - } - - if(typeof jsont === "string"){ - str=jsont; - break; - } - } - if(str){ - return this.fillInBlanks(str,params); - }else{ - throw new Error(msg+" not found") - } - } - static fillInBlanks(msg:string,params:string[]):string{ - //thanks to geotale for the regex - msg=msg.replace(/\$\d+/g,(match) => { - const number=Number(match.slice(1)); - if(params[number-1]){ - return params[number-1]; - }else{ - return match; - } - }); - msg=msg.replace(/{{(.+?)}}/g, - (str, match:string) => { - const [op,strsSplit]=this.fillInBlanks(match,params).split(":"); - const [first,...strs]=strsSplit.split("|"); - switch(op.toUpperCase()){ - case "PLURAL":{ - const numb=Number(first); - if(numb===0){ - return strs[strs.length-1]; - } - return strs[Math.min(strs.length-1,numb-1)]; - } - case "GENDER":{ - if(first==="male"){ - return strs[0]; - }else if(first==="female"){ - return strs[1]; - }else if(first==="neutral"){ - if(strs[2]){ - return strs[2]; - }else{ - return strs[0]; - } - } - } - } - return str; - } - ); - - return msg; - } - static options(){ - return [...langmap.keys()].map(e=>e.replace(".json","")); - } - static setLanguage(lang:string){ - if(this.options().indexOf(userLocale)!==-1){ - localStorage.setItem("lang",lang); - I18n.create(lang); - } - } + return msg; + } + static options() { + return [...langmap.keys()].map((e) => e.replace(".json", "")); + } + static setLanguage(lang: string) { + if (this.options().indexOf(userLocale) !== -1) { + localStorage.setItem("lang", lang); + I18n.create(lang); + } + } } console.log(langmap); -let userLocale = navigator.language.slice(0,2) || "en"; -if(I18n.options().indexOf(userLocale)===-1){ - userLocale="en"; +let userLocale = navigator.language.slice(0, 2) || "en"; +if (I18n.options().indexOf(userLocale) === -1) { + userLocale = "en"; } -const storage=localStorage.getItem("lang"); -if(storage){ - userLocale=storage; -}else{ - localStorage.setItem("lang",userLocale) +const storage = localStorage.getItem("lang"); +if (storage) { + userLocale = storage; +} else { + localStorage.setItem("lang", userLocale); } I18n.create(userLocale); -export{I18n,langmap}; +export {I18n, langmap}; diff --git a/src/webpage/index.html b/src/webpage/index.html index 114e917..584d027 100644 --- a/src/webpage/index.html +++ b/src/webpage/index.html @@ -1,23 +1,37 @@ - + - - + + Jank Client - - - - - - - - + + + + + + + +
- +

Jank Client is loading

This shouldn't take long

Switch Accounts

@@ -36,7 +50,7 @@
- +

USERNAME

@@ -55,8 +69,8 @@ - - + + Channel name @@ -64,15 +78,14 @@ - +
-
-
+
-
+
@@ -81,7 +94,7 @@
-
+