formatting updates
This commit is contained in:
parent
ffe21e6d6c
commit
d2d0f45c81
50 changed files with 7783 additions and 7432 deletions
|
@ -19,6 +19,12 @@
|
||||||
"prettier": "^3.4.2",
|
"prettier": "^3.4.2",
|
||||||
"rimraf": "^6.0.1"
|
"rimraf": "^6.0.1"
|
||||||
},
|
},
|
||||||
|
"prettier":{
|
||||||
|
"useTabs":true,
|
||||||
|
"printWidth":100,
|
||||||
|
"semi":true,
|
||||||
|
"bracketSpacing":false
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.10.0",
|
"@eslint/js": "^9.10.0",
|
||||||
"@html-eslint/eslint-plugin": "^0.25.0",
|
"@html-eslint/eslint-plugin": "^0.25.0",
|
||||||
|
|
148
src/index.ts
148
src/index.ts
|
@ -1,14 +1,14 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import compression from"compression";
|
import compression from "compression";
|
||||||
import express, { Request, Response }from"express";
|
import express, {Request, Response} from "express";
|
||||||
import fs from"node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import path from"node:path";
|
import path from "node:path";
|
||||||
import{ observe, uptime }from"./stats.js";
|
import {observe, uptime} from "./stats.js";
|
||||||
import{ getApiUrls, inviteResponse }from"./utils.js";
|
import {getApiUrls, inviteResponse} from "./utils.js";
|
||||||
import{ fileURLToPath }from"node:url";
|
import {fileURLToPath} from "node:url";
|
||||||
import {readFileSync} from "fs";
|
import {readFileSync} from "fs";
|
||||||
import process from"node:process";
|
import process from "node:process";
|
||||||
|
|
||||||
const devmode = (process.env.NODE_ENV || "development") === "development";
|
const devmode = (process.env.NODE_ENV || "development") === "development";
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
@ -21,156 +21,160 @@ interface Instance {
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
type instace={
|
type instace = {
|
||||||
name:string,
|
name: string;
|
||||||
description?:string,
|
description?: string;
|
||||||
descriptionLong?:string,
|
descriptionLong?: string;
|
||||||
image?:string,
|
image?: string;
|
||||||
url?:string,
|
url?: string;
|
||||||
language:string,
|
language: string;
|
||||||
country:string,
|
country: string;
|
||||||
display:boolean,
|
display: boolean;
|
||||||
urls?:{
|
urls?: {
|
||||||
wellknown:string,
|
wellknown: string;
|
||||||
api:string,
|
api: string;
|
||||||
cdn:string,
|
cdn: string;
|
||||||
gateway:string,
|
gateway: string;
|
||||||
login?:string
|
login?: string;
|
||||||
},
|
};
|
||||||
contactInfo?:{
|
contactInfo?: {
|
||||||
discord?:string,
|
discord?: string;
|
||||||
github?:string,
|
github?: string;
|
||||||
email?:string,
|
email?: string;
|
||||||
spacebar?:string,
|
spacebar?: string;
|
||||||
matrix?:string,
|
matrix?: string;
|
||||||
mastodon?:string
|
mastodon?: string;
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
const instances=JSON.parse(readFileSync(process.env.JANK_INSTANCES_PATH||(__dirname+"/webpage/instances.json")).toString()) as instace[];
|
const instances = JSON.parse(
|
||||||
|
readFileSync(process.env.JANK_INSTANCES_PATH || __dirname + "/webpage/instances.json").toString(),
|
||||||
|
) as instace[];
|
||||||
|
|
||||||
const instanceNames = new Map<string, Instance>();
|
const instanceNames = new Map<string, Instance>();
|
||||||
|
|
||||||
for(const instance of instances){
|
for (const instance of instances) {
|
||||||
instanceNames.set(instance.name, instance);
|
instanceNames.set(instance.name, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
app.use(compression());
|
app.use(compression());
|
||||||
|
|
||||||
async function updateInstances(): Promise<void>{
|
async function updateInstances(): Promise<void> {
|
||||||
try{
|
try {
|
||||||
const response = await fetch("https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/instances/instances.json");
|
const response = await fetch(
|
||||||
|
"https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/instances/instances.json",
|
||||||
|
);
|
||||||
const json = (await response.json()) as Instance[];
|
const json = (await response.json()) as Instance[];
|
||||||
for(const instance of json){
|
for (const instance of json) {
|
||||||
if(instanceNames.has(instance.name)){
|
if (instanceNames.has(instance.name)) {
|
||||||
const existingInstance = instanceNames.get(instance.name);
|
const existingInstance = instanceNames.get(instance.name);
|
||||||
if(existingInstance){
|
if (existingInstance) {
|
||||||
for(const key of Object.keys(instance)){
|
for (const key of Object.keys(instance)) {
|
||||||
if(!existingInstance[key]){
|
if (!existingInstance[key]) {
|
||||||
existingInstance[key] = instance[key];
|
existingInstance[key] = instance[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
instances.push(instance as any);
|
instances.push(instance as any);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
observe(instances);
|
observe(instances);
|
||||||
}catch(error){
|
} catch (error) {
|
||||||
console.error("Error updating instances:", error);
|
console.error("Error updating instances:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateInstances();
|
updateInstances();
|
||||||
|
|
||||||
app.use("/getupdates", async (_req: Request, res: Response)=>{
|
app.use("/getupdates", async (_req: Request, res: Response) => {
|
||||||
try{
|
try {
|
||||||
const stats = await fs.stat(path.join(__dirname, "webpage"));
|
const stats = await fs.stat(path.join(__dirname, "webpage"));
|
||||||
res.send(stats.mtimeMs.toString());
|
res.send(stats.mtimeMs.toString());
|
||||||
}catch(error){
|
} catch (error) {
|
||||||
console.error("Error getting updates:", error);
|
console.error("Error getting updates:", error);
|
||||||
res.status(500).send("Error getting updates");
|
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);
|
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);
|
const instanceUptime = uptime.get(req.query.name as string);
|
||||||
res.send(instanceUptime);
|
res.send(instanceUptime);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use("/", async (req: Request, res: Response)=>{
|
app.use("/", async (req: Request, res: Response) => {
|
||||||
const scheme = req.secure ? "https" : "http";
|
const scheme = req.secure ? "https" : "http";
|
||||||
const host = `${scheme}://${req.get("Host")}`;
|
const host = `${scheme}://${req.get("Host")}`;
|
||||||
const ref = host + req.originalUrl;
|
const ref = host + req.originalUrl;
|
||||||
|
|
||||||
if(host && ref){
|
if (host && ref) {
|
||||||
const link = `${host}/services/oembed?url=${encodeURIComponent(ref)}`;
|
const link = `${host}/services/oembed?url=${encodeURIComponent(ref)}`;
|
||||||
res.set(
|
res.set(
|
||||||
"Link",
|
"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"));
|
res.sendFile(path.join(__dirname, "webpage", "home.html"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(req.path.startsWith("/instances.json")){
|
if (req.path.startsWith("/instances.json")) {
|
||||||
res.json(instances);
|
res.json(instances);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(req.path.startsWith("/invite/")){
|
if (req.path.startsWith("/invite/")) {
|
||||||
res.sendFile(path.join(__dirname, "webpage", "invite.html"));
|
res.sendFile(path.join(__dirname, "webpage", "invite.html"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const filePath = path.join(__dirname, "webpage", req.path);
|
const filePath = path.join(__dirname, "webpage", req.path);
|
||||||
try{
|
try {
|
||||||
await fs.access(filePath);
|
await fs.access(filePath);
|
||||||
if(devmode){
|
if (devmode) {
|
||||||
const filePath2 = path.join(__dirname, "../src/webpage", req.path);
|
const filePath2 = path.join(__dirname, "../src/webpage", req.path);
|
||||||
try{
|
try {
|
||||||
await fs.access(filePath2);
|
await fs.access(filePath2);
|
||||||
res.sendFile(filePath2);
|
res.sendFile(filePath2);
|
||||||
return;
|
return;
|
||||||
}catch{}
|
} catch {}
|
||||||
}
|
}
|
||||||
res.sendFile(filePath);
|
res.sendFile(filePath);
|
||||||
}catch{
|
} catch {
|
||||||
try{
|
try {
|
||||||
await fs.access(`${filePath}.html`);
|
await fs.access(`${filePath}.html`);
|
||||||
if(devmode){
|
if (devmode) {
|
||||||
const filePath2 = path.join(__dirname, "../src/webpage", req.path);
|
const filePath2 = path.join(__dirname, "../src/webpage", req.path);
|
||||||
try{
|
try {
|
||||||
await fs.access(filePath2 + ".html");
|
await fs.access(filePath2 + ".html");
|
||||||
res.sendFile(filePath2 + ".html");
|
res.sendFile(filePath2 + ".html");
|
||||||
return;
|
return;
|
||||||
}catch{}
|
} catch {}
|
||||||
}
|
}
|
||||||
res.sendFile(`${filePath}.html`);
|
res.sendFile(`${filePath}.html`);
|
||||||
}catch{
|
} catch {
|
||||||
if(req.path.startsWith("/src/webpage")){
|
if (req.path.startsWith("/src/webpage")) {
|
||||||
const filePath2 = path.join(__dirname, "..", req.path);
|
const filePath2 = path.join(__dirname, "..", req.path);
|
||||||
try{
|
try {
|
||||||
await fs.access(filePath2);
|
await fs.access(filePath2);
|
||||||
res.sendFile(filePath2);
|
res.sendFile(filePath2);
|
||||||
return;
|
return;
|
||||||
}catch{}
|
} catch {}
|
||||||
}
|
}
|
||||||
res.sendFile(path.join(__dirname, "webpage", "index.html"));
|
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;
|
const PORT = process.env.PORT || Number(process.argv[2]) || 8080;
|
||||||
app.listen(PORT, ()=>{
|
app.listen(PORT, () => {
|
||||||
console.log(`Server running on port ${PORT}`);
|
console.log(`Server running on port ${PORT}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
export{ getApiUrls };
|
export {getApiUrls};
|
||||||
|
|
150
src/stats.ts
150
src/stats.ts
|
@ -1,8 +1,8 @@
|
||||||
import fs from"node:fs";
|
import fs from "node:fs";
|
||||||
import path from"node:path";
|
import path from "node:path";
|
||||||
import{ getApiUrls }from"./utils.js";
|
import {getApiUrls} from "./utils.js";
|
||||||
import{ fileURLToPath }from"node:url";
|
import {fileURLToPath} from "node:url";
|
||||||
import{ setTimeout, clearTimeout }from"node:timers";
|
import {setTimeout, clearTimeout} from "node:timers";
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
@ -14,7 +14,7 @@ interface UptimeEntry {
|
||||||
|
|
||||||
interface Instance {
|
interface Instance {
|
||||||
name: string;
|
name: string;
|
||||||
urls?: { api: string };
|
urls?: {api: string};
|
||||||
url?: string;
|
url?: string;
|
||||||
online?: boolean;
|
online?: boolean;
|
||||||
uptime?: {
|
uptime?: {
|
||||||
|
@ -25,15 +25,15 @@ interface Instance {
|
||||||
}
|
}
|
||||||
|
|
||||||
const uptimeObject: Map<string, UptimeEntry[]> = loadUptimeObject();
|
const uptimeObject: Map<string, UptimeEntry[]> = loadUptimeObject();
|
||||||
export{ uptimeObject as uptime };
|
export {uptimeObject as uptime};
|
||||||
|
|
||||||
function loadUptimeObject(): Map<string, UptimeEntry[]>{
|
function loadUptimeObject(): Map<string, UptimeEntry[]> {
|
||||||
const filePath = process.env.JANK_UPTIME_JSON_PATH||path.join(__dirname, "..", "uptime.json");
|
const filePath = process.env.JANK_UPTIME_JSON_PATH || path.join(__dirname, "..", "uptime.json");
|
||||||
if(fs.existsSync(filePath)){
|
if (fs.existsSync(filePath)) {
|
||||||
try{
|
try {
|
||||||
const data = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
const data = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
||||||
return new Map(Object.entries(data));
|
return new Map(Object.entries(data));
|
||||||
}catch(error){
|
} catch (error) {
|
||||||
console.error("Error reading uptime.json:", error);
|
console.error("Error reading uptime.json:", error);
|
||||||
return new Map();
|
return new Map();
|
||||||
}
|
}
|
||||||
|
@ -43,26 +43,26 @@ function loadUptimeObject(): Map<string, UptimeEntry[]>{
|
||||||
|
|
||||||
let saveTimeout: ReturnType<typeof setTimeout> | null = null;
|
let saveTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
|
||||||
function saveUptimeObject(): void{
|
function saveUptimeObject(): void {
|
||||||
if(saveTimeout){
|
if (saveTimeout) {
|
||||||
clearTimeout(saveTimeout);
|
clearTimeout(saveTimeout);
|
||||||
}
|
}
|
||||||
saveTimeout = setTimeout(()=>{
|
saveTimeout = setTimeout(() => {
|
||||||
const data = Object.fromEntries(uptimeObject);
|
const data = Object.fromEntries(uptimeObject);
|
||||||
fs.writeFile(
|
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),
|
JSON.stringify(data),
|
||||||
error=>{
|
(error) => {
|
||||||
if(error){
|
if (error) {
|
||||||
console.error("Error saving uptime.json:", error);
|
console.error("Error saving uptime.json:", error);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}, 5000); // Batch updates every 5 seconds
|
}, 5000); // Batch updates every 5 seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeUndefinedKey(): void{
|
function removeUndefinedKey(): void {
|
||||||
if(uptimeObject.has("undefined")){
|
if (uptimeObject.has("undefined")) {
|
||||||
uptimeObject.delete("undefined");
|
uptimeObject.delete("undefined");
|
||||||
saveUptimeObject();
|
saveUptimeObject();
|
||||||
}
|
}
|
||||||
|
@ -70,101 +70,89 @@ function removeUndefinedKey(): void{
|
||||||
|
|
||||||
removeUndefinedKey();
|
removeUndefinedKey();
|
||||||
|
|
||||||
export async function observe(instances: Instance[]): Promise<void>{
|
export async function observe(instances: Instance[]): Promise<void> {
|
||||||
const activeInstances = new Set<string>();
|
const activeInstances = new Set<string>();
|
||||||
const instancePromises = instances.map(instance=>resolveInstance(instance, activeInstances)
|
const instancePromises = instances.map((instance) => resolveInstance(instance, activeInstances));
|
||||||
);
|
|
||||||
await Promise.allSettled(instancePromises);
|
await Promise.allSettled(instancePromises);
|
||||||
updateInactiveInstances(activeInstances);
|
updateInactiveInstances(activeInstances);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resolveInstance(
|
async function resolveInstance(instance: Instance, activeInstances: Set<string>): Promise<void> {
|
||||||
instance: Instance,
|
try {
|
||||||
activeInstances: Set<string>
|
|
||||||
): Promise<void>{
|
|
||||||
try{
|
|
||||||
calcStats(instance);
|
calcStats(instance);
|
||||||
const api = await getApiUrl(instance);
|
const api = await getApiUrl(instance);
|
||||||
if(!api){
|
if (!api) {
|
||||||
handleUnresolvedApi(instance);
|
handleUnresolvedApi(instance);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
activeInstances.add(instance.name);
|
activeInstances.add(instance.name);
|
||||||
await checkHealth(instance, api);
|
await checkHealth(instance, api);
|
||||||
scheduleHealthCheck(instance, api);
|
scheduleHealthCheck(instance, api);
|
||||||
}catch(error){
|
} catch (error) {
|
||||||
console.error("Error resolving instance:", error);
|
console.error("Error resolving instance:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getApiUrl(instance: Instance): Promise<string | null>{
|
async function getApiUrl(instance: Instance): Promise<string | null> {
|
||||||
if(instance.urls){
|
if (instance.urls) {
|
||||||
return instance.urls.api;
|
return instance.urls.api;
|
||||||
}
|
}
|
||||||
if(instance.url){
|
if (instance.url) {
|
||||||
const urls = await getApiUrls(instance.url);
|
const urls = await getApiUrls(instance.url);
|
||||||
return urls ? urls.api : null;
|
return urls ? urls.api : null;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleUnresolvedApi(instance: Instance): void{
|
function handleUnresolvedApi(instance: Instance): void {
|
||||||
setStatus(instance, false);
|
setStatus(instance, false);
|
||||||
console.warn(`${instance.name} does not resolve api URL`, instance);
|
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 checkInterval = 1000 * 60 * 30;
|
||||||
const initialDelay = Math.random() * 1000 * 60 * 10;
|
const initialDelay = Math.random() * 1000 * 60 * 10;
|
||||||
setTimeout(()=>{
|
setTimeout(() => {
|
||||||
checkHealth(instance, api);
|
checkHealth(instance, api);
|
||||||
setInterval(()=>checkHealth(instance, api), checkInterval);
|
setInterval(() => checkHealth(instance, api), checkInterval);
|
||||||
}, initialDelay);
|
}, initialDelay);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkHealth(
|
async function checkHealth(instance: Instance, api: string, tries = 0): Promise<void> {
|
||||||
instance: Instance,
|
try {
|
||||||
api: string,
|
const response = await fetch(`${api}/ping`, {method: "HEAD"});
|
||||||
tries = 0
|
|
||||||
): Promise<void>{
|
|
||||||
try{
|
|
||||||
const response = await fetch(`${api}/ping`, { method: "HEAD" });
|
|
||||||
console.log(`Checking health for ${instance.name}: ${response.status}`);
|
console.log(`Checking health for ${instance.name}: ${response.status}`);
|
||||||
if(response.ok || tries > 3){
|
if (response.ok || tries > 3) {
|
||||||
setStatus(instance, response.ok);
|
setStatus(instance, response.ok);
|
||||||
}else{
|
} else {
|
||||||
retryHealthCheck(instance, api, tries);
|
retryHealthCheck(instance, api, tries);
|
||||||
}
|
}
|
||||||
}catch(error){
|
} catch (error) {
|
||||||
console.error(`Error checking health for ${instance.name}:`, error);
|
console.error(`Error checking health for ${instance.name}:`, error);
|
||||||
if(tries > 3){
|
if (tries > 3) {
|
||||||
setStatus(instance, false);
|
setStatus(instance, false);
|
||||||
}else{
|
} else {
|
||||||
retryHealthCheck(instance, api, tries);
|
retryHealthCheck(instance, api, tries);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function retryHealthCheck(
|
function retryHealthCheck(instance: Instance, api: string, tries: number): void {
|
||||||
instance: Instance,
|
setTimeout(() => checkHealth(instance, api, tries + 1), 30000);
|
||||||
api: string,
|
|
||||||
tries: number
|
|
||||||
): void{
|
|
||||||
setTimeout(()=>checkHealth(instance, api, tries + 1), 30000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateInactiveInstances(activeInstances: Set<string>): void{
|
function updateInactiveInstances(activeInstances: Set<string>): void {
|
||||||
for(const key of uptimeObject.keys()){
|
for (const key of uptimeObject.keys()) {
|
||||||
if(!activeInstances.has(key)){
|
if (!activeInstances.has(key)) {
|
||||||
setStatus(key, false);
|
setStatus(key, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function calcStats(instance: Instance): void{
|
function calcStats(instance: Instance): void {
|
||||||
const obj = uptimeObject.get(instance.name);
|
const obj = uptimeObject.get(instance.name);
|
||||||
if(!obj)return;
|
if (!obj) return;
|
||||||
|
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const day = now - 1000 * 60 * 60 * 24;
|
const day = now - 1000 * 60 * 60 * 24;
|
||||||
|
@ -176,7 +164,7 @@ function calcStats(instance: Instance): void{
|
||||||
let weektime = 0;
|
let weektime = 0;
|
||||||
let online = false;
|
let online = false;
|
||||||
|
|
||||||
for(let i = 0; i < obj.length; i++){
|
for (let i = 0; i < obj.length; i++) {
|
||||||
const entry = obj[i];
|
const entry = obj[i];
|
||||||
online = entry.online;
|
online = entry.online;
|
||||||
const stamp = entry.time;
|
const stamp = entry.time;
|
||||||
|
@ -186,11 +174,11 @@ function calcStats(instance: Instance): void{
|
||||||
totalTimePassed += timePassed;
|
totalTimePassed += timePassed;
|
||||||
alltime += Number(online) * timePassed;
|
alltime += Number(online) * timePassed;
|
||||||
|
|
||||||
if(stamp + timePassed > week){
|
if (stamp + timePassed > week) {
|
||||||
const weekTimePassed = Math.min(timePassed, nextStamp - week);
|
const weekTimePassed = Math.min(timePassed, nextStamp - week);
|
||||||
weektime += Number(online) * weekTimePassed;
|
weektime += Number(online) * weekTimePassed;
|
||||||
|
|
||||||
if(stamp + timePassed > day){
|
if (stamp + timePassed > day) {
|
||||||
const dayTimePassed = Math.min(weekTimePassed, nextStamp - day);
|
const dayTimePassed = Math.min(weekTimePassed, nextStamp - day);
|
||||||
daytime += Number(online) * dayTimePassed;
|
daytime += Number(online) * dayTimePassed;
|
||||||
}
|
}
|
||||||
|
@ -198,13 +186,7 @@ function calcStats(instance: Instance): void{
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.online = online;
|
instance.online = online;
|
||||||
instance.uptime = calculateUptimeStats(
|
instance.uptime = calculateUptimeStats(totalTimePassed, alltime, daytime, weektime, online);
|
||||||
totalTimePassed,
|
|
||||||
alltime,
|
|
||||||
daytime,
|
|
||||||
weektime,
|
|
||||||
online
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateUptimeStats(
|
function calculateUptimeStats(
|
||||||
|
@ -212,46 +194,46 @@ function calculateUptimeStats(
|
||||||
alltime: number,
|
alltime: number,
|
||||||
daytime: number,
|
daytime: number,
|
||||||
weektime: number,
|
weektime: number,
|
||||||
online: boolean
|
online: boolean,
|
||||||
): { daytime: number; weektime: number; alltime: number }{
|
): {daytime: number; weektime: number; alltime: number} {
|
||||||
const dayInMs = 1000 * 60 * 60 * 24;
|
const dayInMs = 1000 * 60 * 60 * 24;
|
||||||
const weekInMs = dayInMs * 7;
|
const weekInMs = dayInMs * 7;
|
||||||
|
|
||||||
alltime /= totalTimePassed;
|
alltime /= totalTimePassed;
|
||||||
|
|
||||||
if(totalTimePassed > dayInMs){
|
if (totalTimePassed > dayInMs) {
|
||||||
daytime = daytime || (online ? dayInMs : 0);
|
daytime = daytime || (online ? dayInMs : 0);
|
||||||
daytime /= dayInMs;
|
daytime /= dayInMs;
|
||||||
|
|
||||||
if(totalTimePassed > weekInMs){
|
if (totalTimePassed > weekInMs) {
|
||||||
weektime = weektime || (online ? weekInMs : 0);
|
weektime = weektime || (online ? weekInMs : 0);
|
||||||
weektime /= weekInMs;
|
weektime /= weekInMs;
|
||||||
}else{
|
} else {
|
||||||
weektime = alltime;
|
weektime = alltime;
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
weektime = alltime;
|
weektime = alltime;
|
||||||
daytime = 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;
|
const name = typeof instance === "string" ? instance : instance.name;
|
||||||
let obj = uptimeObject.get(name);
|
let obj = uptimeObject.get(name);
|
||||||
|
|
||||||
if(!obj){
|
if (!obj) {
|
||||||
obj = [];
|
obj = [];
|
||||||
uptimeObject.set(name, obj);
|
uptimeObject.set(name, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastEntry = obj.at(-1);
|
const lastEntry = obj.at(-1);
|
||||||
if(!lastEntry || lastEntry.online !== status){
|
if (!lastEntry || lastEntry.online !== status) {
|
||||||
obj.push({ time: Date.now(), online: status });
|
obj.push({time: Date.now(), online: status});
|
||||||
saveUptimeObject();
|
saveUptimeObject();
|
||||||
|
|
||||||
if(typeof instance !== "string"){
|
if (typeof instance !== "string") {
|
||||||
calcStats(instance);
|
calcStats(instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
38
src/utils.ts
38
src/utils.ts
|
@ -1,4 +1,4 @@
|
||||||
import{ Request, Response }from"express";
|
import {Request, Response} from "express";
|
||||||
|
|
||||||
interface ApiUrls {
|
interface ApiUrls {
|
||||||
api: string;
|
api: string;
|
||||||
|
@ -19,56 +19,58 @@ interface Invite {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getApiUrls(url: string): Promise<ApiUrls | null>{
|
export async function getApiUrls(url: string): Promise<ApiUrls | null> {
|
||||||
if(!url.endsWith("/")){
|
if (!url.endsWith("/")) {
|
||||||
url += "/";
|
url += "/";
|
||||||
}
|
}
|
||||||
try{
|
try {
|
||||||
const info: ApiUrls = await fetch(`${url}.well-known/spacebar`).then(res=>res.json());
|
const info: ApiUrls = await fetch(`${url}.well-known/spacebar`).then((res) => res.json());
|
||||||
const api = info.api;
|
const api = info.api;
|
||||||
const apiUrl = new URL(api);
|
const apiUrl = new URL(api);
|
||||||
const policies: any = await fetch(
|
const policies: any = await fetch(
|
||||||
`${api}${apiUrl.pathname.includes("api") ? "" : "api"}/policies/instance/domains`
|
`${api}${apiUrl.pathname.includes("api") ? "" : "api"}/policies/instance/domains`,
|
||||||
).then(res=>res.json());
|
).then((res) => res.json());
|
||||||
return{
|
return {
|
||||||
api: policies.apiEndpoint,
|
api: policies.apiEndpoint,
|
||||||
gateway: policies.gateway,
|
gateway: policies.gateway,
|
||||||
cdn: policies.cdn,
|
cdn: policies.cdn,
|
||||||
wellknown: url,
|
wellknown: url,
|
||||||
};
|
};
|
||||||
}catch(error){
|
} catch (error) {
|
||||||
console.error("Error fetching API URLs:", error);
|
console.error("Error fetching API URLs:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function inviteResponse(req: Request, res: Response): Promise<void>{
|
export async function inviteResponse(req: Request, res: Response): Promise<void> {
|
||||||
let url: URL;
|
let url: URL;
|
||||||
try{
|
try {
|
||||||
url = new URL(req.query.url as string);
|
url = new URL(req.query.url as string);
|
||||||
}catch{
|
} catch {
|
||||||
const scheme = req.secure ? "https" : "http";
|
const scheme = req.secure ? "https" : "http";
|
||||||
const host = `${scheme}://${req.get("Host")}`;
|
const host = `${scheme}://${req.get("Host")}`;
|
||||||
url = new URL(host);
|
url = new URL(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
try{
|
try {
|
||||||
if(url.pathname.startsWith("invite")){
|
if (url.pathname.startsWith("invite")) {
|
||||||
throw new Error("Invalid invite URL");
|
throw new Error("Invalid invite URL");
|
||||||
}
|
}
|
||||||
|
|
||||||
const code = url.pathname.split("/")[2];
|
const code = url.pathname.split("/")[2];
|
||||||
const instance = url.searchParams.get("instance");
|
const instance = url.searchParams.get("instance");
|
||||||
if(!instance){
|
if (!instance) {
|
||||||
throw new Error("Instance not specified");
|
throw new Error("Instance not specified");
|
||||||
}
|
}
|
||||||
|
|
||||||
const urls = await getApiUrls(instance);
|
const urls = await getApiUrls(instance);
|
||||||
if(!urls){
|
if (!urls) {
|
||||||
throw new Error("Failed to get API URLs");
|
throw new Error("Failed to get API URLs");
|
||||||
}
|
}
|
||||||
|
|
||||||
const invite = await fetch(`${urls.api}/invites/${code}`).then(json=>json.json() as Promise<Invite>);
|
const invite = await fetch(`${urls.api}/invites/${code}`).then(
|
||||||
|
(json) => json.json() as Promise<Invite>,
|
||||||
|
);
|
||||||
const title = invite.guild.name;
|
const title = invite.guild.name;
|
||||||
const description = invite.inviter
|
const description = invite.inviter
|
||||||
? `${invite.inviter.username} has invited you to ${invite.guild.name}${invite.guild.description ? `\n${invite.guild.description}` : ""}`
|
? `${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<void>
|
||||||
thumbnail,
|
thumbnail,
|
||||||
description,
|
description,
|
||||||
});
|
});
|
||||||
}catch(error){
|
} catch (error) {
|
||||||
console.error("Error processing invite response:", error);
|
console.error("Error processing invite response:", error);
|
||||||
res.json({
|
res.json({
|
||||||
type: "link",
|
type: "link",
|
||||||
|
|
|
@ -1,33 +1,33 @@
|
||||||
import { BinRead } from "../utils/binaryUtils.js";
|
import {BinRead} from "../utils/binaryUtils.js";
|
||||||
import { Track } from "./track.js";
|
import {Track} from "./track.js";
|
||||||
|
|
||||||
export class Audio{
|
export class Audio {
|
||||||
name:string;
|
name: string;
|
||||||
tracks:(Track|number)[];
|
tracks: (Track | number)[];
|
||||||
constructor(name:string,tracks:(Track|number)[]){
|
constructor(name: string, tracks: (Track | number)[]) {
|
||||||
this.tracks=tracks;
|
this.tracks = tracks;
|
||||||
this.name=name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
static parse(read:BinRead,trackarr:Track[]):Audio{
|
static parse(read: BinRead, trackarr: Track[]): Audio {
|
||||||
const name=read.readString8();
|
const name = read.readString8();
|
||||||
const length=read.read16();
|
const length = read.read16();
|
||||||
const tracks:(Track|number)[]=[]
|
const tracks: (Track | number)[] = [];
|
||||||
for(let i=0;i<length;i++){
|
for (let i = 0; i < length; i++) {
|
||||||
let index=read.read16();
|
let index = read.read16();
|
||||||
if(index===0){
|
if (index === 0) {
|
||||||
tracks.push(read.readFloat32());
|
tracks.push(read.readFloat32());
|
||||||
}else{
|
} else {
|
||||||
tracks.push(trackarr[index-1]);
|
tracks.push(trackarr[index - 1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new Audio(name,tracks)
|
return new Audio(name, tracks);
|
||||||
}
|
}
|
||||||
async play(){
|
async play() {
|
||||||
for(const thing of this.tracks){
|
for (const thing of this.tracks) {
|
||||||
if(thing instanceof Track){
|
if (thing instanceof Track) {
|
||||||
thing.play();
|
thing.play();
|
||||||
}else{
|
} else {
|
||||||
await new Promise((res)=>setTimeout(res,thing));
|
await new Promise((res) => setTimeout(res, thing));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,39 @@
|
||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Jank Audio</title>
|
<title>Jank Audio</title>
|
||||||
<meta content="Jank Sound" property="og:title">
|
<meta content="Jank Sound" property="og:title" />
|
||||||
<meta content="A sound editor for jank clients sound format .jasf" property="og:description">
|
<meta content="A sound editor for jank clients sound format .jasf" property="og:description" />
|
||||||
<meta content="/logo.webp" property="og:image">
|
<meta content="/logo.webp" property="og:image" />
|
||||||
<meta content="#4b458c" data-react-helmet="true" name="theme-color">
|
<meta content="#4b458c" data-react-helmet="true" name="theme-color" />
|
||||||
<link href="/style.css" rel="stylesheet">
|
<link href="/style.css" rel="stylesheet" />
|
||||||
<link href="/themes.css" rel="stylesheet" id="lightcss">
|
<link href="/themes.css" rel="stylesheet" id="lightcss" />
|
||||||
<style>body.no-theme{background:#16191b;}@media(prefers-color-scheme:light){body.no-theme{background:#9397bd;}}</style>
|
<style>
|
||||||
|
body.no-theme {
|
||||||
|
background: #16191b;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
body.no-theme {
|
||||||
|
background: #9397bd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="no-theme" style="overflow-y: scroll;">
|
<body class="no-theme" style="overflow-y: scroll">
|
||||||
<h1>This will eventually be something</h1>
|
<h1>This will eventually be something</h1>
|
||||||
<p>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.</p>
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
<h3>why does this tool need to exist?</h3>
|
<h3>why does this tool need to exist?</h3>
|
||||||
<p>For size reasons jank does not use normal sound files, so I need to make this whole format to be more adaptable</p>
|
<p>
|
||||||
|
For size reasons jank does not use normal sound files, so I need to make this whole format to
|
||||||
|
be more adaptable
|
||||||
|
</p>
|
||||||
<button id="download">Download the sounds</button>
|
<button id="download">Download the sounds</button>
|
||||||
</body>
|
</body>
|
||||||
<script src="/audio/page.js" type="module"></script>
|
<script src="/audio/page.js" type="module"></script>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { BinWrite } from "../utils/binaryUtils.js";
|
import {BinWrite} from "../utils/binaryUtils.js";
|
||||||
import { setTheme } from "../utils/utils.js";
|
import {setTheme} from "../utils/utils.js";
|
||||||
import { Play } from "./play.js";
|
import {Play} from "./play.js";
|
||||||
|
|
||||||
setTheme();
|
setTheme();
|
||||||
const w=new BinWrite(2**12);
|
const w = new BinWrite(2 ** 12);
|
||||||
w.writeStringNo("jasf");
|
w.writeStringNo("jasf");
|
||||||
w.write8(4);
|
w.write8(4);
|
||||||
|
|
||||||
|
@ -18,40 +18,43 @@ w.writeString8("custom");
|
||||||
w.write32Float(150);
|
w.write32Float(150);
|
||||||
//return Math.sin(((t + 2) ** Math.cos(t * 4)) * Math.PI * 2 * freq);
|
//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)))
|
//Math.sin((((t+2)**Math.cos((t*4)))*((Math.PI*2)*f)))
|
||||||
w.write8(4);//sin
|
w.write8(4); //sin
|
||||||
w.write8(5)//times
|
w.write8(5); //times
|
||||||
{
|
{
|
||||||
w.write8(9);//Power
|
w.write8(9); //Power
|
||||||
|
|
||||||
{
|
{
|
||||||
w.write8(6);//adding
|
w.write8(6); //adding
|
||||||
w.write8(1);//t
|
w.write8(1); //t
|
||||||
w.write8(0);w.write32Float(2);//2
|
w.write8(0);
|
||||||
|
w.write32Float(2); //2
|
||||||
}
|
}
|
||||||
w.write8(13);//cos
|
w.write8(13); //cos
|
||||||
w.write8(5);// times
|
w.write8(5); // times
|
||||||
w.write8(1);//t
|
w.write8(1); //t
|
||||||
w.write8(0);w.write32Float(4);//4
|
w.write8(0);
|
||||||
|
w.write32Float(4); //4
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
w.write8(5)//times
|
w.write8(5); //times
|
||||||
w.write8(5)//times
|
w.write8(5); //times
|
||||||
w.write8(3);//PI
|
w.write8(3); //PI
|
||||||
w.write8(0);w.write32Float(2);//2
|
w.write8(0);
|
||||||
w.write8(2);//freq
|
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.write8(4);
|
||||||
w.write32Float(1)
|
w.write32Float(1);
|
||||||
w.write32Float(700)
|
w.write32Float(700);
|
||||||
|
|
||||||
w.write16(3);//beep
|
w.write16(3); //beep
|
||||||
{
|
{
|
||||||
w.write8(1);
|
w.write8(1);
|
||||||
w.write32Float(1)
|
w.write32Float(1);
|
||||||
w.write32Float(700);
|
w.write32Float(700);
|
||||||
w.write32Float(50);
|
w.write32Float(50);
|
||||||
|
|
||||||
|
@ -59,15 +62,15 @@ w.write16(3);//beep
|
||||||
w.write32Float(100);
|
w.write32Float(100);
|
||||||
|
|
||||||
w.write8(1);
|
w.write8(1);
|
||||||
w.write32Float(1)
|
w.write32Float(1);
|
||||||
w.write32Float(700);
|
w.write32Float(700);
|
||||||
w.write32Float(50);
|
w.write32Float(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
w.write16(5);//three
|
w.write16(5); //three
|
||||||
{
|
{
|
||||||
w.write8(1);
|
w.write8(1);
|
||||||
w.write32Float(1)
|
w.write32Float(1);
|
||||||
w.write32Float(800);
|
w.write32Float(800);
|
||||||
w.write32Float(50);
|
w.write32Float(50);
|
||||||
|
|
||||||
|
@ -75,7 +78,7 @@ w.write16(5);//three
|
||||||
w.write32Float(50);
|
w.write32Float(50);
|
||||||
|
|
||||||
w.write8(1);
|
w.write8(1);
|
||||||
w.write32Float(1)
|
w.write32Float(1);
|
||||||
w.write32Float(1000);
|
w.write32Float(1000);
|
||||||
w.write32Float(50);
|
w.write32Float(50);
|
||||||
|
|
||||||
|
@ -83,15 +86,15 @@ w.write16(5);//three
|
||||||
w.write32Float(50);
|
w.write32Float(50);
|
||||||
|
|
||||||
w.write8(1);
|
w.write8(1);
|
||||||
w.write32Float(1)
|
w.write32Float(1);
|
||||||
w.write32Float(1300);
|
w.write32Float(1300);
|
||||||
w.write32Float(50);
|
w.write32Float(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
w.write16(5);//square
|
w.write16(5); //square
|
||||||
{
|
{
|
||||||
w.write8(3);
|
w.write8(3);
|
||||||
w.write32Float(1)
|
w.write32Float(1);
|
||||||
w.write32Float(600);
|
w.write32Float(600);
|
||||||
w.write32Float(50);
|
w.write32Float(50);
|
||||||
|
|
||||||
|
@ -99,7 +102,7 @@ w.write16(5);//square
|
||||||
w.write32Float(50);
|
w.write32Float(50);
|
||||||
|
|
||||||
w.write8(3);
|
w.write8(3);
|
||||||
w.write32Float(1)
|
w.write32Float(1);
|
||||||
w.write32Float(800);
|
w.write32Float(800);
|
||||||
w.write32Float(50);
|
w.write32Float(50);
|
||||||
|
|
||||||
|
@ -107,11 +110,11 @@ w.write16(5);//square
|
||||||
w.write32Float(50);
|
w.write32Float(50);
|
||||||
|
|
||||||
w.write8(3);
|
w.write8(3);
|
||||||
w.write32Float(1)
|
w.write32Float(1);
|
||||||
w.write32Float(1000);
|
w.write32Float(1000);
|
||||||
w.write32Float(50);
|
w.write32Float(50);
|
||||||
}
|
}
|
||||||
w.write16(4);//2 audio
|
w.write16(4); //2 audio
|
||||||
|
|
||||||
w.writeString8("zip");
|
w.writeString8("zip");
|
||||||
w.write16(1);
|
w.write16(1);
|
||||||
|
@ -128,8 +131,8 @@ w.write16(3);
|
||||||
w.writeString8("square");
|
w.writeString8("square");
|
||||||
w.write16(1);
|
w.write16(1);
|
||||||
w.write16(4);
|
w.write16(4);
|
||||||
const buff=w.getBuffer();
|
const buff = w.getBuffer();
|
||||||
const play=Play.parseBin(buff);
|
const play = Play.parseBin(buff);
|
||||||
/*
|
/*
|
||||||
const zip=play.audios.get("square");
|
const zip=play.audios.get("square");
|
||||||
if(zip){
|
if(zip){
|
||||||
|
@ -140,18 +143,18 @@ if(zip){
|
||||||
console.log(play.voices[3][0].info.wave)
|
console.log(play.voices[3][0].info.wave)
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
console.log(play,buff);
|
console.log(play, buff);
|
||||||
|
|
||||||
const download=document.getElementById("download");
|
const download = document.getElementById("download");
|
||||||
if(download){
|
if (download) {
|
||||||
download.onclick=()=>{
|
download.onclick = () => {
|
||||||
const blob = new Blob([buff], { type: "binary" });
|
const blob = new Blob([buff], {type: "binary"});
|
||||||
const downloadUrl = URL.createObjectURL(blob);
|
const downloadUrl = URL.createObjectURL(blob);
|
||||||
const a = document.createElement('a');
|
const a = document.createElement("a");
|
||||||
a.href = downloadUrl;
|
a.href = downloadUrl;
|
||||||
a.download = "sounds.jasf";
|
a.download = "sounds.jasf";
|
||||||
document.body.appendChild(a);
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
URL.revokeObjectURL(downloadUrl);
|
URL.revokeObjectURL(downloadUrl);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,48 +1,48 @@
|
||||||
import { BinRead } from "../utils/binaryUtils.js";
|
import {BinRead} from "../utils/binaryUtils.js";
|
||||||
import { Track } from "./track.js";
|
import {Track} from "./track.js";
|
||||||
import { AVoice } from "./voice.js";
|
import {AVoice} from "./voice.js";
|
||||||
import { Audio } from "./audio.js";
|
import {Audio} from "./audio.js";
|
||||||
export class Play{
|
export class Play {
|
||||||
voices:[AVoice,string][]
|
voices: [AVoice, string][];
|
||||||
tracks:Track[]
|
tracks: Track[];
|
||||||
audios:Map<string,Audio>;
|
audios: Map<string, Audio>;
|
||||||
constructor(voices:[AVoice,string][],tracks:Track[],audios:Map<string,Audio>){
|
constructor(voices: [AVoice, string][], tracks: Track[], audios: Map<string, Audio>) {
|
||||||
this.voices=voices;
|
this.voices = voices;
|
||||||
this.tracks=tracks;
|
this.tracks = tracks;
|
||||||
this.audios=audios;
|
this.audios = audios;
|
||||||
}
|
}
|
||||||
static parseBin(buffer:ArrayBuffer){
|
static parseBin(buffer: ArrayBuffer) {
|
||||||
const read=new BinRead(buffer);
|
const read = new BinRead(buffer);
|
||||||
if(read.readStringNo(4)!=="jasf") throw new Error("this is not a jasf file");
|
if (read.readStringNo(4) !== "jasf") throw new Error("this is not a jasf file");
|
||||||
let voices=read.read8();
|
let voices = read.read8();
|
||||||
let six=false;
|
let six = false;
|
||||||
if(voices===255){
|
if (voices === 255) {
|
||||||
voices=read.read16();
|
voices = read.read16();
|
||||||
six=true;
|
six = true;
|
||||||
}
|
}
|
||||||
const voiceArr:[AVoice,string][]=[];
|
const voiceArr: [AVoice, string][] = [];
|
||||||
for(let i=0;i<voices;i++){
|
for (let i = 0; i < voices; i++) {
|
||||||
voiceArr.push(AVoice.getVoice(read));
|
voiceArr.push(AVoice.getVoice(read));
|
||||||
}
|
}
|
||||||
|
|
||||||
const tracks=read.read16();
|
const tracks = read.read16();
|
||||||
const trackArr:Track[]=[];
|
const trackArr: Track[] = [];
|
||||||
for(let i=0;i<tracks;i++){
|
for (let i = 0; i < tracks; i++) {
|
||||||
trackArr.push(Track.parse(read,voiceArr,six));
|
trackArr.push(Track.parse(read, voiceArr, six));
|
||||||
}
|
}
|
||||||
|
|
||||||
const audios=read.read16();
|
const audios = read.read16();
|
||||||
const audioArr=new Map<string,Audio>();
|
const audioArr = new Map<string, Audio>();
|
||||||
for(let i=0;i<audios;i++){
|
for (let i = 0; i < audios; i++) {
|
||||||
const a=Audio.parse(read,trackArr);
|
const a = Audio.parse(read, trackArr);
|
||||||
audioArr.set(a.name,a)
|
audioArr.set(a.name, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Play(voiceArr,trackArr,audioArr);
|
return new Play(voiceArr, trackArr, audioArr);
|
||||||
}
|
}
|
||||||
static async playURL(url:string){
|
static async playURL(url: string) {
|
||||||
const res=await fetch(url);
|
const res = await fetch(url);
|
||||||
const arr=await res.arrayBuffer();
|
const arr = await res.arrayBuffer();
|
||||||
return this.parseBin(arr);
|
return this.parseBin(arr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +1,44 @@
|
||||||
import { BinRead } from "../utils/binaryUtils.js";
|
import {BinRead} from "../utils/binaryUtils.js";
|
||||||
import { AVoice } from "./voice.js";
|
import {AVoice} from "./voice.js";
|
||||||
|
|
||||||
export class Track{
|
export class Track {
|
||||||
seq:(AVoice|number)[];
|
seq: (AVoice | number)[];
|
||||||
constructor(playing:(AVoice|number)[]){
|
constructor(playing: (AVoice | number)[]) {
|
||||||
this.seq=playing;
|
this.seq = playing;
|
||||||
}
|
}
|
||||||
static parse(read:BinRead,play:[AVoice,string][],six:boolean):Track{
|
static parse(read: BinRead, play: [AVoice, string][], six: boolean): Track {
|
||||||
const length=read.read16();
|
const length = read.read16();
|
||||||
const play2:(AVoice|number)[]=[];
|
const play2: (AVoice | number)[] = [];
|
||||||
for(let i=0;i<length;i++){
|
for (let i = 0; i < length; i++) {
|
||||||
let index:number;
|
let index: number;
|
||||||
if(six){
|
if (six) {
|
||||||
index=read.read16();
|
index = read.read16();
|
||||||
}else{
|
} else {
|
||||||
index=read.read8();
|
index = read.read8();
|
||||||
}
|
}
|
||||||
if(index===0){
|
if (index === 0) {
|
||||||
play2.push(read.readFloat32());
|
play2.push(read.readFloat32());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
index--;
|
index--;
|
||||||
if(!play[index]) throw new Error("voice not found");
|
if (!play[index]) throw new Error("voice not found");
|
||||||
const [voice]=play[index];
|
const [voice] = play[index];
|
||||||
let temp:AVoice;
|
let temp: AVoice;
|
||||||
if((voice.info.wave instanceof Function)){
|
if (voice.info.wave instanceof Function) {
|
||||||
temp=voice.clone(read.readFloat32(),read.readFloat32());
|
temp = voice.clone(read.readFloat32(), read.readFloat32());
|
||||||
}else{
|
} else {
|
||||||
temp=voice.clone(read.readFloat32(),read.readFloat32(),read.readFloat32());
|
temp = voice.clone(read.readFloat32(), read.readFloat32(), read.readFloat32());
|
||||||
}
|
}
|
||||||
play2.push(temp);
|
play2.push(temp);
|
||||||
|
|
||||||
}
|
}
|
||||||
return new Track(play2);
|
return new Track(play2);
|
||||||
}
|
}
|
||||||
async play(){
|
async play() {
|
||||||
for(const thing of this.seq){
|
for (const thing of this.seq) {
|
||||||
if(thing instanceof AVoice){
|
if (thing instanceof AVoice) {
|
||||||
thing.playL();
|
thing.playL();
|
||||||
}else{
|
} else {
|
||||||
await new Promise((res)=>setTimeout(res,thing));
|
await new Promise((res) => setTimeout(res, thing));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
import { BinRead } from "../utils/binaryUtils.js";
|
import {BinRead} from "../utils/binaryUtils.js";
|
||||||
|
|
||||||
class AVoice{
|
class AVoice {
|
||||||
audioCtx: AudioContext;
|
audioCtx: AudioContext;
|
||||||
info: { wave: string | Function; freq: number };
|
info: {wave: string | Function; freq: number};
|
||||||
playing: boolean;
|
playing: boolean;
|
||||||
myArrayBuffer: AudioBuffer;
|
myArrayBuffer: AudioBuffer;
|
||||||
gainNode: GainNode;
|
gainNode: GainNode;
|
||||||
buffer: Float32Array;
|
buffer: Float32Array;
|
||||||
source: AudioBufferSourceNode;
|
source: AudioBufferSourceNode;
|
||||||
length=1;
|
length = 1;
|
||||||
constructor(wave: string | Function, freq: number, volume = 1,length=1000){
|
constructor(wave: string | Function, freq: number, volume = 1, length = 1000) {
|
||||||
this.length=length;
|
this.length = length;
|
||||||
this.audioCtx = new window.AudioContext();
|
this.audioCtx = new window.AudioContext();
|
||||||
this.info = { wave, freq };
|
this.info = {wave, freq};
|
||||||
this.playing = false;
|
this.playing = false;
|
||||||
this.myArrayBuffer = this.audioCtx.createBuffer(
|
this.myArrayBuffer = this.audioCtx.createBuffer(
|
||||||
1,
|
1,
|
||||||
this.audioCtx.sampleRate*length/1000,
|
(this.audioCtx.sampleRate * length) / 1000,
|
||||||
this.audioCtx.sampleRate
|
this.audioCtx.sampleRate,
|
||||||
);
|
);
|
||||||
this.gainNode = this.audioCtx.createGain();
|
this.gainNode = this.audioCtx.createGain();
|
||||||
this.gainNode.gain.value = volume;
|
this.gainNode.gain.value = volume;
|
||||||
|
@ -29,193 +29,193 @@ class AVoice{
|
||||||
this.source.start();
|
this.source.start();
|
||||||
this.updateWave();
|
this.updateWave();
|
||||||
}
|
}
|
||||||
clone(volume:number,freq:number,length=this.length){
|
clone(volume: number, freq: number, length = this.length) {
|
||||||
return new AVoice(this.wave,freq,volume,length);
|
return new AVoice(this.wave, freq, volume, length);
|
||||||
}
|
}
|
||||||
get wave(): string | Function{
|
get wave(): string | Function {
|
||||||
return this.info.wave;
|
return this.info.wave;
|
||||||
}
|
}
|
||||||
get freq(): number{
|
get freq(): number {
|
||||||
return this.info.freq;
|
return this.info.freq;
|
||||||
}
|
}
|
||||||
set wave(wave: string | Function){
|
set wave(wave: string | Function) {
|
||||||
this.info.wave = wave;
|
this.info.wave = wave;
|
||||||
this.updateWave();
|
this.updateWave();
|
||||||
}
|
}
|
||||||
set freq(freq: number){
|
set freq(freq: number) {
|
||||||
this.info.freq = freq;
|
this.info.freq = freq;
|
||||||
this.updateWave();
|
this.updateWave();
|
||||||
}
|
}
|
||||||
updateWave(): void{
|
updateWave(): void {
|
||||||
const func = this.waveFunction();
|
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);
|
this.buffer[i] = func(i / this.audioCtx.sampleRate, this.freq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
waveFunction(): Function{
|
waveFunction(): Function {
|
||||||
if(typeof this.wave === "function"){
|
if (typeof this.wave === "function") {
|
||||||
return this.wave;
|
return this.wave;
|
||||||
}
|
}
|
||||||
switch(this.wave){
|
switch (this.wave) {
|
||||||
case"sin":
|
case "sin":
|
||||||
return(t: number, freq: number)=>{
|
return (t: number, freq: number) => {
|
||||||
return Math.sin(t * Math.PI * 2 * freq);
|
return Math.sin(t * Math.PI * 2 * freq);
|
||||||
};
|
};
|
||||||
case"triangle":
|
case "triangle":
|
||||||
return(t: number, freq: number)=>{
|
return (t: number, freq: number) => {
|
||||||
return Math.abs(((4 * t * freq) % 4) - 2) - 1;
|
return Math.abs(((4 * t * freq) % 4) - 2) - 1;
|
||||||
};
|
};
|
||||||
case"sawtooth":
|
case "sawtooth":
|
||||||
return(t: number, freq: number)=>{
|
return (t: number, freq: number) => {
|
||||||
return((t * freq) % 1) * 2 - 1;
|
return ((t * freq) % 1) * 2 - 1;
|
||||||
};
|
};
|
||||||
case"square":
|
case "square":
|
||||||
return(t: number, freq: number)=>{
|
return (t: number, freq: number) => {
|
||||||
return(t * freq) % 2 < 1 ? 1 : -1;
|
return (t * freq) % 2 < 1 ? 1 : -1;
|
||||||
};
|
};
|
||||||
case"white":
|
case "white":
|
||||||
return(_t: number, _freq: number)=>{
|
return (_t: number, _freq: number) => {
|
||||||
return Math.random() * 2 - 1;
|
return Math.random() * 2 - 1;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return new Function();
|
return new Function();
|
||||||
}
|
}
|
||||||
play(): void{
|
play(): void {
|
||||||
if(this.playing){
|
if (this.playing) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.source.connect(this.gainNode);
|
this.source.connect(this.gainNode);
|
||||||
this.playing = true;
|
this.playing = true;
|
||||||
}
|
}
|
||||||
playL(){
|
playL() {
|
||||||
this.play();
|
this.play();
|
||||||
setTimeout(()=>{
|
setTimeout(() => {
|
||||||
this.stop();
|
this.stop();
|
||||||
},this.length);
|
}, this.length);
|
||||||
}
|
}
|
||||||
stop(): void{
|
stop(): void {
|
||||||
if(this.playing){
|
if (this.playing) {
|
||||||
this.source.disconnect();
|
this.source.disconnect();
|
||||||
this.playing = false;
|
this.playing = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static noises(noise: string): void{
|
static noises(noise: string): void {
|
||||||
switch(noise){
|
switch (noise) {
|
||||||
case"three": {
|
case "three": {
|
||||||
const voicy = new AVoice("sin", 800);
|
const voicy = new AVoice("sin", 800);
|
||||||
voicy.play();
|
voicy.play();
|
||||||
setTimeout(_=>{
|
setTimeout((_) => {
|
||||||
voicy.freq = 1000;
|
voicy.freq = 1000;
|
||||||
}, 50);
|
}, 50);
|
||||||
setTimeout(_=>{
|
setTimeout((_) => {
|
||||||
voicy.freq = 1300;
|
voicy.freq = 1300;
|
||||||
}, 100);
|
}, 100);
|
||||||
setTimeout(_=>{
|
setTimeout((_) => {
|
||||||
voicy.stop();
|
voicy.stop();
|
||||||
}, 150);
|
}, 150);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case"zip": {
|
case "zip": {
|
||||||
const voicy = new AVoice((t: number, freq: number)=>{
|
const voicy = new AVoice((t: number, freq: number) => {
|
||||||
return Math.sin((t + 2) ** Math.cos(t * 4) * Math.PI * 2 * freq);
|
return Math.sin((t + 2) ** Math.cos(t * 4) * Math.PI * 2 * freq);
|
||||||
}, 700);
|
}, 700);
|
||||||
voicy.play();
|
voicy.play();
|
||||||
setTimeout(_=>{
|
setTimeout((_) => {
|
||||||
voicy.stop();
|
voicy.stop();
|
||||||
}, 150);
|
}, 150);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case"square": {
|
case "square": {
|
||||||
const voicy = new AVoice("square", 600, 0.4);
|
const voicy = new AVoice("square", 600, 0.4);
|
||||||
voicy.play();
|
voicy.play();
|
||||||
setTimeout(_=>{
|
setTimeout((_) => {
|
||||||
voicy.freq = 800;
|
voicy.freq = 800;
|
||||||
}, 50);
|
}, 50);
|
||||||
setTimeout(_=>{
|
setTimeout((_) => {
|
||||||
voicy.freq = 1000;
|
voicy.freq = 1000;
|
||||||
}, 100);
|
}, 100);
|
||||||
setTimeout(_=>{
|
setTimeout((_) => {
|
||||||
voicy.stop();
|
voicy.stop();
|
||||||
}, 150);
|
}, 150);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case"beep": {
|
case "beep": {
|
||||||
const voicy = new AVoice("sin", 800);
|
const voicy = new AVoice("sin", 800);
|
||||||
voicy.play();
|
voicy.play();
|
||||||
setTimeout(_=>{
|
setTimeout((_) => {
|
||||||
voicy.stop();
|
voicy.stop();
|
||||||
}, 50);
|
}, 50);
|
||||||
setTimeout(_=>{
|
setTimeout((_) => {
|
||||||
voicy.play();
|
voicy.play();
|
||||||
}, 100);
|
}, 100);
|
||||||
setTimeout(_=>{
|
setTimeout((_) => {
|
||||||
voicy.stop();
|
voicy.stop();
|
||||||
}, 150);
|
}, 150);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "join":{
|
case "join": {
|
||||||
const voicy = new AVoice("triangle", 600,.1);
|
const voicy = new AVoice("triangle", 600, 0.1);
|
||||||
voicy.play();
|
voicy.play();
|
||||||
setTimeout(_=>{
|
setTimeout((_) => {
|
||||||
voicy.freq=800;
|
voicy.freq = 800;
|
||||||
}, 75);
|
}, 75);
|
||||||
setTimeout(_=>{
|
setTimeout((_) => {
|
||||||
voicy.freq=1000;
|
voicy.freq = 1000;
|
||||||
}, 150);
|
}, 150);
|
||||||
setTimeout(_=>{
|
setTimeout((_) => {
|
||||||
voicy.stop();
|
voicy.stop();
|
||||||
}, 200);
|
}, 200);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "leave":{
|
case "leave": {
|
||||||
const voicy = new AVoice("triangle", 850,.5);
|
const voicy = new AVoice("triangle", 850, 0.5);
|
||||||
voicy.play();
|
voicy.play();
|
||||||
setTimeout(_=>{
|
setTimeout((_) => {
|
||||||
voicy.freq=700;
|
voicy.freq = 700;
|
||||||
}, 100);
|
}, 100);
|
||||||
setTimeout(_=>{
|
setTimeout((_) => {
|
||||||
voicy.stop();
|
voicy.stop();
|
||||||
voicy.freq=400;
|
voicy.freq = 400;
|
||||||
}, 180);
|
}, 180);
|
||||||
setTimeout(_=>{
|
setTimeout((_) => {
|
||||||
voicy.play();
|
voicy.play();
|
||||||
}, 200);
|
}, 200);
|
||||||
setTimeout(_=>{
|
setTimeout((_) => {
|
||||||
voicy.stop();
|
voicy.stop();
|
||||||
}, 250);
|
}, 250);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static get sounds(){
|
static get sounds() {
|
||||||
return["three", "zip", "square", "beep"];
|
return ["three", "zip", "square", "beep"];
|
||||||
}
|
}
|
||||||
static getVoice(read:BinRead):[AVoice,string]{
|
static getVoice(read: BinRead): [AVoice, string] {
|
||||||
const name = read.readString8();
|
const name = read.readString8();
|
||||||
let length=read.readFloat32();
|
let length = read.readFloat32();
|
||||||
let special:Function|string
|
let special: Function | string;
|
||||||
if(length!==0){
|
if (length !== 0) {
|
||||||
special=this.parseExpression(read);
|
special = this.parseExpression(read);
|
||||||
}else{
|
} else {
|
||||||
special=name;
|
special = name;
|
||||||
length=1;
|
length = 1;
|
||||||
}
|
}
|
||||||
return [new AVoice(special,0,0,length),name]
|
return [new AVoice(special, 0, 0, length), name];
|
||||||
}
|
}
|
||||||
static parseExpression(read:BinRead):Function{
|
static parseExpression(read: BinRead): Function {
|
||||||
return new Function("t","f",`return ${this.PEHelper(read)};`);
|
return new Function("t", "f", `return ${this.PEHelper(read)};`);
|
||||||
}
|
}
|
||||||
static PEHelper(read:BinRead):string{
|
static PEHelper(read: BinRead): string {
|
||||||
let state=read.read8();
|
let state = read.read8();
|
||||||
switch(state){
|
switch (state) {
|
||||||
case 0:
|
case 0:
|
||||||
return ""+read.readFloat32();
|
return "" + read.readFloat32();
|
||||||
case 1:
|
case 1:
|
||||||
return "t";
|
return "t";
|
||||||
case 2:
|
case 2:
|
||||||
return "f";
|
return "f";
|
||||||
case 3:
|
case 3:
|
||||||
return `Math.PI`
|
return `Math.PI`;
|
||||||
case 4:
|
case 4:
|
||||||
return `Math.sin(${this.PEHelper(read)})`;
|
return `Math.sin(${this.PEHelper(read)})`;
|
||||||
case 5:
|
case 5:
|
||||||
|
@ -238,9 +238,8 @@ class AVoice{
|
||||||
return `Math.cos(${this.PEHelper(read)})`;
|
return `Math.cos(${this.PEHelper(read)})`;
|
||||||
default:
|
default:
|
||||||
throw new Error("unexpected case found!");
|
throw new Error("unexpected case found!");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export{ AVoice as AVoice };
|
export {AVoice as AVoice};
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,82 +1,82 @@
|
||||||
import{ iOS }from"./utils/utils.js";
|
import {iOS} from "./utils/utils.js";
|
||||||
class Contextmenu<x, y>{
|
class Contextmenu<x, y> {
|
||||||
static currentmenu: HTMLElement | "";
|
static currentmenu: HTMLElement | "";
|
||||||
name: string;
|
name: string;
|
||||||
buttons: [
|
buttons: [
|
||||||
string|(()=>string),
|
string | (() => string),
|
||||||
(this: x, arg: y, e: MouseEvent) => void,
|
(this: x, arg: y, e: MouseEvent) => void,
|
||||||
string | null,
|
string | null,
|
||||||
(this: x, arg: y) => boolean,
|
(this: x, arg: y) => boolean,
|
||||||
(this: x, arg: y) => boolean,
|
(this: x, arg: y) => boolean,
|
||||||
string
|
string,
|
||||||
][];
|
][];
|
||||||
div!: HTMLDivElement;
|
div!: HTMLDivElement;
|
||||||
static setup(){
|
static setup() {
|
||||||
Contextmenu.currentmenu = "";
|
Contextmenu.currentmenu = "";
|
||||||
document.addEventListener("click", event=>{
|
document.addEventListener("click", (event) => {
|
||||||
if(Contextmenu.currentmenu === ""){
|
if (Contextmenu.currentmenu === "") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(!Contextmenu.currentmenu.contains(event.target as Node)){
|
if (!Contextmenu.currentmenu.contains(event.target as Node)) {
|
||||||
Contextmenu.currentmenu.remove();
|
Contextmenu.currentmenu.remove();
|
||||||
Contextmenu.currentmenu = "";
|
Contextmenu.currentmenu = "";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
constructor(name: string){
|
constructor(name: string) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.buttons = [];
|
this.buttons = [];
|
||||||
}
|
}
|
||||||
addbutton(
|
addbutton(
|
||||||
text: string|(()=>string),
|
text: string | (() => string),
|
||||||
onclick: (this: x, arg: y, e: MouseEvent) => void,
|
onclick: (this: x, arg: y, e: MouseEvent) => void,
|
||||||
img: null | string = null,
|
img: null | string = null,
|
||||||
shown: (this: x, arg: y) => boolean = _=>true,
|
shown: (this: x, arg: y) => boolean = (_) => true,
|
||||||
enabled: (this: x, arg: y) => boolean = _=>true
|
enabled: (this: x, arg: y) => boolean = (_) => true,
|
||||||
){
|
) {
|
||||||
this.buttons.push([text, onclick, img, shown, enabled, "button"]);
|
this.buttons.push([text, onclick, img, shown, enabled, "button"]);
|
||||||
return{};
|
return {};
|
||||||
}
|
}
|
||||||
addsubmenu(
|
addsubmenu(
|
||||||
text: string|(()=>string),
|
text: string | (() => string),
|
||||||
onclick: (this: x, arg: y, e: MouseEvent) => void,
|
onclick: (this: x, arg: y, e: MouseEvent) => void,
|
||||||
img = null,
|
img = null,
|
||||||
shown: (this: x, arg: y) => boolean = _=>true,
|
shown: (this: x, arg: y) => boolean = (_) => true,
|
||||||
enabled: (this: x, arg: y) => boolean = _=>true
|
enabled: (this: x, arg: y) => boolean = (_) => true,
|
||||||
){
|
) {
|
||||||
this.buttons.push([text, onclick, img, shown, enabled, "submenu"]);
|
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");
|
const div = document.createElement("div");
|
||||||
div.classList.add("contextmenu", "flexttb");
|
div.classList.add("contextmenu", "flexttb");
|
||||||
|
|
||||||
let visibleButtons = 0;
|
let visibleButtons = 0;
|
||||||
for(const thing of this.buttons){
|
for (const thing of this.buttons) {
|
||||||
if(!thing[3].call(addinfo, other))continue;
|
if (!thing[3].call(addinfo, other)) continue;
|
||||||
visibleButtons++;
|
visibleButtons++;
|
||||||
|
|
||||||
const intext = document.createElement("button");
|
const intext = document.createElement("button");
|
||||||
intext.disabled = !thing[4].call(addinfo, other);
|
intext.disabled = !thing[4].call(addinfo, other);
|
||||||
intext.classList.add("contextbutton");
|
intext.classList.add("contextbutton");
|
||||||
if(thing[0] instanceof Function){
|
if (thing[0] instanceof Function) {
|
||||||
intext.textContent = thing[0]();
|
intext.textContent = thing[0]();
|
||||||
}else{
|
} else {
|
||||||
intext.textContent = thing[0];
|
intext.textContent = thing[0];
|
||||||
}
|
}
|
||||||
console.log(thing);
|
console.log(thing);
|
||||||
if(thing[5] === "button" || thing[5] === "submenu"){
|
if (thing[5] === "button" || thing[5] === "submenu") {
|
||||||
intext.onclick = (e)=>{
|
intext.onclick = (e) => {
|
||||||
div.remove();
|
div.remove();
|
||||||
thing[1].call(addinfo, other,e)
|
thing[1].call(addinfo, other, e);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
div.appendChild(intext);
|
div.appendChild(intext);
|
||||||
}
|
}
|
||||||
if(visibleButtons == 0)return;
|
if (visibleButtons == 0) return;
|
||||||
|
|
||||||
if(Contextmenu.currentmenu != ""){
|
if (Contextmenu.currentmenu != "") {
|
||||||
Contextmenu.currentmenu.remove();
|
Contextmenu.currentmenu.remove();
|
||||||
}
|
}
|
||||||
div.style.top = y + "px";
|
div.style.top = y + "px";
|
||||||
|
@ -87,64 +87,74 @@ class Contextmenu<x, y>{
|
||||||
Contextmenu.currentmenu = div;
|
Contextmenu.currentmenu = div;
|
||||||
return this.div;
|
return this.div;
|
||||||
}
|
}
|
||||||
bindContextmenu(obj: HTMLElement, addinfo: x, other: y,touchDrag:(x:number,y:number)=>unknown=()=>{},touchEnd:(x:number,y:number)=>unknown=()=>{}){
|
bindContextmenu(
|
||||||
const func = (event: MouseEvent)=>{
|
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.preventDefault();
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
this.makemenu(event.clientX, event.clientY, addinfo, other);
|
this.makemenu(event.clientX, event.clientY, addinfo, other);
|
||||||
};
|
};
|
||||||
obj.addEventListener("contextmenu", func);
|
obj.addEventListener("contextmenu", func);
|
||||||
if(iOS){
|
if (iOS) {
|
||||||
let hold:NodeJS.Timeout|undefined;
|
let hold: NodeJS.Timeout | undefined;
|
||||||
let x!:number;
|
let x!: number;
|
||||||
let y!:number;
|
let y!: number;
|
||||||
obj.addEventListener("touchstart",(event: TouchEvent)=>{
|
obj.addEventListener(
|
||||||
x=event.touches[0].pageX;
|
"touchstart",
|
||||||
y=event.touches[0].pageY;
|
(event: TouchEvent) => {
|
||||||
if(event.touches.length > 1){
|
x = event.touches[0].pageX;
|
||||||
|
y = event.touches[0].pageY;
|
||||||
|
if (event.touches.length > 1) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
this.makemenu(event.touches[0].clientX, event.touches[0].clientY, addinfo, other);
|
this.makemenu(event.touches[0].clientX, event.touches[0].clientY, addinfo, other);
|
||||||
}else{
|
} else {
|
||||||
//
|
//
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
hold=setTimeout(()=>{
|
hold = setTimeout(() => {
|
||||||
if(lastx**2+lasty**2>10**2) return;
|
if (lastx ** 2 + lasty ** 2 > 10 ** 2) return;
|
||||||
this.makemenu(event.touches[0].clientX, event.touches[0].clientY, addinfo, other);
|
this.makemenu(event.touches[0].clientX, event.touches[0].clientY, addinfo, other);
|
||||||
console.log(obj);
|
console.log(obj);
|
||||||
},500)
|
}, 500);
|
||||||
}
|
}
|
||||||
},{passive: false});
|
},
|
||||||
let lastx=0;
|
{passive: false},
|
||||||
let lasty=0;
|
);
|
||||||
obj.addEventListener("touchend",()=>{
|
let lastx = 0;
|
||||||
if(hold){
|
let lasty = 0;
|
||||||
|
obj.addEventListener("touchend", () => {
|
||||||
|
if (hold) {
|
||||||
clearTimeout(hold);
|
clearTimeout(hold);
|
||||||
}
|
}
|
||||||
touchEnd(lastx,lasty);
|
touchEnd(lastx, lasty);
|
||||||
});
|
});
|
||||||
obj.addEventListener("touchmove",(event)=>{
|
obj.addEventListener("touchmove", (event) => {
|
||||||
lastx=event.touches[0].pageX-x;
|
lastx = event.touches[0].pageX - x;
|
||||||
lasty=event.touches[0].pageY-y;
|
lasty = event.touches[0].pageY - y;
|
||||||
touchDrag(lastx,lasty);
|
touchDrag(lastx, lasty);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return func;
|
return func;
|
||||||
}
|
}
|
||||||
static keepOnScreen(obj: HTMLElement){
|
static keepOnScreen(obj: HTMLElement) {
|
||||||
const html = document.documentElement.getBoundingClientRect();
|
const html = document.documentElement.getBoundingClientRect();
|
||||||
const docheight = html.height;
|
const docheight = html.height;
|
||||||
const docwidth = html.width;
|
const docwidth = html.width;
|
||||||
const box = obj.getBoundingClientRect();
|
const box = obj.getBoundingClientRect();
|
||||||
console.log(box, docheight, docwidth);
|
console.log(box, docheight, docwidth);
|
||||||
if(box.right > docwidth){
|
if (box.right > docwidth) {
|
||||||
console.log("test");
|
console.log("test");
|
||||||
obj.style.left = docwidth - box.width + "px";
|
obj.style.left = docwidth - box.width + "px";
|
||||||
}
|
}
|
||||||
if(box.bottom > docheight){
|
if (box.bottom > docheight) {
|
||||||
obj.style.top = docheight - box.height + "px";
|
obj.style.top = docheight - box.height + "px";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Contextmenu.setup();
|
Contextmenu.setup();
|
||||||
export{ Contextmenu };
|
export {Contextmenu};
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
import{ Guild }from"./guild.js";
|
import {Guild} from "./guild.js";
|
||||||
import{ Channel }from"./channel.js";
|
import {Channel} from "./channel.js";
|
||||||
import{ Message }from"./message.js";
|
import {Message} from "./message.js";
|
||||||
import{ Localuser }from"./localuser.js";
|
import {Localuser} from "./localuser.js";
|
||||||
import{ User }from"./user.js";
|
import {User} from "./user.js";
|
||||||
import{channeljson,dirrectjson,memberjson,messagejson}from"./jsontypes.js";
|
import {channeljson, dirrectjson, memberjson, messagejson} from "./jsontypes.js";
|
||||||
import{ Permissions }from"./permissions.js";
|
import {Permissions} from "./permissions.js";
|
||||||
import{ SnowFlake }from"./snowflake.js";
|
import {SnowFlake} from "./snowflake.js";
|
||||||
import{ Contextmenu }from"./contextmenu.js";
|
import {Contextmenu} from "./contextmenu.js";
|
||||||
import { I18n } from "./i18n.js";
|
import {I18n} from "./i18n.js";
|
||||||
import { Float, FormError } from "./settings.js";
|
import {Float, FormError} from "./settings.js";
|
||||||
|
|
||||||
class Direct extends Guild{
|
class Direct extends Guild {
|
||||||
declare channelids: { [key: string]: Group };
|
declare channelids: {[key: string]: Group};
|
||||||
channels: Group[];
|
channels: Group[];
|
||||||
getUnixTime(): number{
|
getUnixTime(): number {
|
||||||
throw new Error("Do not call this for Direct, it does not make sense");
|
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);
|
super(-1, owner, null);
|
||||||
this.message_notifications = 0;
|
this.message_notifications = 0;
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
|
@ -29,7 +29,7 @@ class Direct extends Guild{
|
||||||
this.roleids = new Map();
|
this.roleids = new Map();
|
||||||
this.prevchannel = undefined;
|
this.prevchannel = undefined;
|
||||||
this.properties.name = I18n.getTranslation("DMs.name");
|
this.properties.name = I18n.getTranslation("DMs.name");
|
||||||
for(const thing of json){
|
for (const thing of json) {
|
||||||
const temp = new Group(thing, this);
|
const temp = new Group(thing, this);
|
||||||
this.channels.push(temp);
|
this.channels.push(temp);
|
||||||
this.channelids[temp.id] = temp;
|
this.channelids[temp.id] = temp;
|
||||||
|
@ -37,7 +37,7 @@ class Direct extends Guild{
|
||||||
}
|
}
|
||||||
this.headchannels = this.channels;
|
this.headchannels = this.channels;
|
||||||
}
|
}
|
||||||
createChannelpac(json: any){
|
createChannelpac(json: any) {
|
||||||
const thischannel = new Group(json, this);
|
const thischannel = new Group(json, this);
|
||||||
this.channelids[thischannel.id] = thischannel;
|
this.channelids[thischannel.id] = thischannel;
|
||||||
this.channels.push(thischannel);
|
this.channels.push(thischannel);
|
||||||
|
@ -46,266 +46,270 @@ class Direct extends Guild{
|
||||||
this.printServers();
|
this.printServers();
|
||||||
return thischannel;
|
return thischannel;
|
||||||
}
|
}
|
||||||
delChannel(json: channeljson){
|
delChannel(json: channeljson) {
|
||||||
const channel = this.channelids[json.id];
|
const channel = this.channelids[json.id];
|
||||||
super.delChannel(json);
|
super.delChannel(json);
|
||||||
if(channel){
|
if (channel) {
|
||||||
channel.del();
|
channel.del();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getHTML(){
|
getHTML() {
|
||||||
const ddiv=document.createElement("div");
|
const ddiv = document.createElement("div");
|
||||||
const build=super.getHTML();
|
const build = super.getHTML();
|
||||||
const freindDiv=document.createElement("div");
|
const freindDiv = document.createElement("div");
|
||||||
freindDiv.classList.add("liststyle","flexltr","friendsbutton");
|
freindDiv.classList.add("liststyle", "flexltr", "friendsbutton");
|
||||||
|
|
||||||
const icon=document.createElement("span");
|
const icon = document.createElement("span");
|
||||||
icon.classList.add("svgicon","svg-friends","space");
|
icon.classList.add("svgicon", "svg-friends", "space");
|
||||||
freindDiv.append(icon);
|
freindDiv.append(icon);
|
||||||
|
|
||||||
freindDiv.append(I18n.getTranslation("friends.friends"));
|
freindDiv.append(I18n.getTranslation("friends.friends"));
|
||||||
ddiv.append(freindDiv);
|
ddiv.append(freindDiv);
|
||||||
freindDiv.onclick=()=>{
|
freindDiv.onclick = () => {
|
||||||
this.loadChannel(null);
|
this.loadChannel(null);
|
||||||
}
|
};
|
||||||
|
|
||||||
ddiv.append(build);
|
ddiv.append(build);
|
||||||
return ddiv;
|
return ddiv;
|
||||||
}
|
}
|
||||||
noChannel(addstate:boolean){
|
noChannel(addstate: boolean) {
|
||||||
if(addstate){
|
if (addstate) {
|
||||||
history.pushState([this.id,undefined], "", "/channels/" + this.id);
|
history.pushState([this.id, undefined], "", "/channels/" + this.id);
|
||||||
}
|
}
|
||||||
this.localuser.pageTitle(I18n.getTranslation("friends.friendlist"));
|
this.localuser.pageTitle(I18n.getTranslation("friends.friendlist"));
|
||||||
const channelTopic = document.getElementById("channelTopic") as HTMLSpanElement;
|
const channelTopic = document.getElementById("channelTopic") as HTMLSpanElement;
|
||||||
channelTopic.removeAttribute("hidden");
|
channelTopic.removeAttribute("hidden");
|
||||||
channelTopic.textContent="";
|
channelTopic.textContent = "";
|
||||||
|
|
||||||
const loading = document.getElementById("loadingdiv") as HTMLDivElement;
|
const loading = document.getElementById("loadingdiv") as HTMLDivElement;
|
||||||
loading.classList.remove("loading");
|
loading.classList.remove("loading");
|
||||||
this.localuser.getSidePannel();
|
this.localuser.getSidePannel();
|
||||||
|
|
||||||
const messages = document.getElementById("channelw") as HTMLDivElement;
|
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();
|
thing.remove();
|
||||||
}
|
}
|
||||||
const container=document.createElement("div");
|
const container = document.createElement("div");
|
||||||
container.classList.add("messagecontainer","flexttb","friendcontainer")
|
container.classList.add("messagecontainer", "flexttb", "friendcontainer");
|
||||||
|
|
||||||
messages.append(container);
|
messages.append(container);
|
||||||
const checkVoid=()=>{
|
const checkVoid = () => {
|
||||||
if(this.localuser.channelfocus!==undefined||this.localuser.lookingguild!==this){
|
if (this.localuser.channelfocus !== undefined || this.localuser.lookingguild !== this) {
|
||||||
this.localuser.relationshipsUpdate=()=>{};
|
this.localuser.relationshipsUpdate = () => {};
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
function genuserstrip(user:User,icons:HTMLElement):HTMLElement{
|
function genuserstrip(user: User, icons: HTMLElement): HTMLElement {
|
||||||
const div=document.createElement("div");
|
const div = document.createElement("div");
|
||||||
div.classList.add("flexltr","liststyle");
|
div.classList.add("flexltr", "liststyle");
|
||||||
user.bind(div);
|
user.bind(div);
|
||||||
div.append(user.buildpfp());
|
div.append(user.buildpfp());
|
||||||
|
|
||||||
const userinfos=document.createElement("div");
|
const userinfos = document.createElement("div");
|
||||||
userinfos.classList.add("flexttb");
|
userinfos.classList.add("flexttb");
|
||||||
const username=document.createElement("span");
|
const username = document.createElement("span");
|
||||||
username.textContent=user.name;
|
username.textContent = user.name;
|
||||||
userinfos.append(username,user.getStatus());
|
userinfos.append(username, user.getStatus());
|
||||||
div.append(userinfos);
|
div.append(userinfos);
|
||||||
User.contextmenu.bindContextmenu(div,user,undefined);
|
User.contextmenu.bindContextmenu(div, user, undefined);
|
||||||
userinfos.style.flexGrow="1";
|
userinfos.style.flexGrow = "1";
|
||||||
|
|
||||||
div.append(icons);
|
div.append(icons);
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
//TODO update on users coming online
|
//TODO update on users coming online
|
||||||
const online=document.createElement("button");
|
const online = document.createElement("button");
|
||||||
online.textContent=I18n.getTranslation("friends.online");
|
online.textContent = I18n.getTranslation("friends.online");
|
||||||
channelTopic.append(online);
|
channelTopic.append(online);
|
||||||
const genOnline=()=>{
|
const genOnline = () => {
|
||||||
this.localuser.relationshipsUpdate=genOnline;
|
this.localuser.relationshipsUpdate = genOnline;
|
||||||
checkVoid();
|
checkVoid();
|
||||||
container.innerHTML="";
|
container.innerHTML = "";
|
||||||
container.append(I18n.getTranslation("friends.online:"));
|
container.append(I18n.getTranslation("friends.online:"));
|
||||||
for(const user of this.localuser.inrelation){
|
for (const user of this.localuser.inrelation) {
|
||||||
if(user.relationshipType===1&&user.online){
|
if (user.relationshipType === 1 && user.online) {
|
||||||
const buttonc=document.createElement("div");
|
const buttonc = document.createElement("div");
|
||||||
const button1=document.createElement("span");
|
const button1 = document.createElement("span");
|
||||||
button1.classList.add("svg-frmessage","svgicon");
|
button1.classList.add("svg-frmessage", "svgicon");
|
||||||
buttonc.append(button1);
|
buttonc.append(button1);
|
||||||
buttonc.classList.add("friendlyButton");
|
buttonc.classList.add("friendlyButton");
|
||||||
buttonc.onclick=(e)=>{
|
buttonc.onclick = (e) => {
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
user.opendm();
|
user.opendm();
|
||||||
}
|
};
|
||||||
container.append(genuserstrip(user,buttonc));
|
container.append(genuserstrip(user, buttonc));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
online.onclick=genOnline;
|
online.onclick = genOnline;
|
||||||
genOnline();
|
genOnline();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const all=document.createElement("button");
|
const all = document.createElement("button");
|
||||||
all.textContent=I18n.getTranslation("friends.all");
|
all.textContent = I18n.getTranslation("friends.all");
|
||||||
const genAll=()=>{
|
const genAll = () => {
|
||||||
this.localuser.relationshipsUpdate=genAll;
|
this.localuser.relationshipsUpdate = genAll;
|
||||||
checkVoid();
|
checkVoid();
|
||||||
container.innerHTML="";
|
container.innerHTML = "";
|
||||||
container.append(I18n.getTranslation("friends.all:"));
|
container.append(I18n.getTranslation("friends.all:"));
|
||||||
for(const user of this.localuser.inrelation){
|
for (const user of this.localuser.inrelation) {
|
||||||
if(user.relationshipType===1){
|
if (user.relationshipType === 1) {
|
||||||
const buttonc=document.createElement("div");
|
const buttonc = document.createElement("div");
|
||||||
const button1=document.createElement("span");
|
const button1 = document.createElement("span");
|
||||||
button1.classList.add("svg-frmessage","svgicon");
|
button1.classList.add("svg-frmessage", "svgicon");
|
||||||
buttonc.append(button1);
|
buttonc.append(button1);
|
||||||
buttonc.classList.add("friendlyButton");
|
buttonc.classList.add("friendlyButton");
|
||||||
buttonc.onclick=(e)=>{
|
buttonc.onclick = (e) => {
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
user.opendm();
|
user.opendm();
|
||||||
}
|
};
|
||||||
container.append(genuserstrip(user,buttonc));
|
container.append(genuserstrip(user, buttonc));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
all.onclick=genAll;
|
all.onclick = genAll;
|
||||||
channelTopic.append(all);
|
channelTopic.append(all);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const pending=document.createElement("button");
|
const pending = document.createElement("button");
|
||||||
pending.textContent=I18n.getTranslation("friends.pending");
|
pending.textContent = I18n.getTranslation("friends.pending");
|
||||||
const genPending=()=>{
|
const genPending = () => {
|
||||||
this.localuser.relationshipsUpdate=genPending;
|
this.localuser.relationshipsUpdate = genPending;
|
||||||
checkVoid();
|
checkVoid();
|
||||||
container.innerHTML="";
|
container.innerHTML = "";
|
||||||
container.append(I18n.getTranslation("friends.pending:"));
|
container.append(I18n.getTranslation("friends.pending:"));
|
||||||
for(const user of this.localuser.inrelation){
|
for (const user of this.localuser.inrelation) {
|
||||||
if(user.relationshipType===3||user.relationshipType===4){
|
if (user.relationshipType === 3 || user.relationshipType === 4) {
|
||||||
const buttons=document.createElement("div");
|
const buttons = document.createElement("div");
|
||||||
buttons.classList.add("flexltr");
|
buttons.classList.add("flexltr");
|
||||||
const buttonc=document.createElement("div");
|
const buttonc = document.createElement("div");
|
||||||
const button1=document.createElement("span");
|
const button1 = document.createElement("span");
|
||||||
button1.classList.add("svgicon","svg-x");
|
button1.classList.add("svgicon", "svg-x");
|
||||||
if(user.relationshipType===3){
|
if (user.relationshipType === 3) {
|
||||||
const buttonc=document.createElement("div");
|
const buttonc = document.createElement("div");
|
||||||
const button2=document.createElement("span");
|
const button2 = document.createElement("span");
|
||||||
button2.classList.add("svgicon","svg-x");
|
button2.classList.add("svgicon", "svg-x");
|
||||||
button2.classList.add("svg-addfriend");
|
button2.classList.add("svg-addfriend");
|
||||||
buttonc.append(button2);
|
buttonc.append(button2);
|
||||||
buttonc.classList.add("friendlyButton");
|
buttonc.classList.add("friendlyButton");
|
||||||
buttonc.append(button2);
|
buttonc.append(button2);
|
||||||
buttons.append(buttonc);
|
buttons.append(buttonc);
|
||||||
buttonc.onclick=(e)=>{
|
buttonc.onclick = (e) => {
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
user.changeRelationship(1);
|
user.changeRelationship(1);
|
||||||
outerDiv.remove();
|
outerDiv.remove();
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
buttonc.append(button1);
|
buttonc.append(button1);
|
||||||
buttonc.classList.add("friendlyButton");
|
buttonc.classList.add("friendlyButton");
|
||||||
buttonc.onclick=(e)=>{
|
buttonc.onclick = (e) => {
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
user.changeRelationship(0);
|
user.changeRelationship(0);
|
||||||
outerDiv.remove();
|
outerDiv.remove();
|
||||||
}
|
};
|
||||||
buttons.append(buttonc);
|
buttons.append(buttonc);
|
||||||
const outerDiv=genuserstrip(user,buttons);
|
const outerDiv = genuserstrip(user, buttons);
|
||||||
container.append(outerDiv);
|
container.append(outerDiv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
pending.onclick=genPending;
|
pending.onclick = genPending;
|
||||||
channelTopic.append(pending);
|
channelTopic.append(pending);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const blocked=document.createElement("button");
|
const blocked = document.createElement("button");
|
||||||
blocked.textContent=I18n.getTranslation("friends.blocked");
|
blocked.textContent = I18n.getTranslation("friends.blocked");
|
||||||
|
|
||||||
const genBlocked=()=>{
|
const genBlocked = () => {
|
||||||
this.localuser.relationshipsUpdate=genBlocked;
|
this.localuser.relationshipsUpdate = genBlocked;
|
||||||
checkVoid();
|
checkVoid();
|
||||||
container.innerHTML="";
|
container.innerHTML = "";
|
||||||
container.append(I18n.getTranslation("friends.blockedusers"));
|
container.append(I18n.getTranslation("friends.blockedusers"));
|
||||||
for(const user of this.localuser.inrelation){
|
for (const user of this.localuser.inrelation) {
|
||||||
if(user.relationshipType===2){
|
if (user.relationshipType === 2) {
|
||||||
const buttonc=document.createElement("div");
|
const buttonc = document.createElement("div");
|
||||||
const button1=document.createElement("span");
|
const button1 = document.createElement("span");
|
||||||
button1.classList.add("svg-x","svgicon");
|
button1.classList.add("svg-x", "svgicon");
|
||||||
buttonc.append(button1);
|
buttonc.append(button1);
|
||||||
buttonc.classList.add("friendlyButton");
|
buttonc.classList.add("friendlyButton");
|
||||||
buttonc.onclick=(e)=>{
|
buttonc.onclick = (e) => {
|
||||||
user.changeRelationship(0);
|
user.changeRelationship(0);
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
outerDiv.remove();
|
outerDiv.remove();
|
||||||
}
|
};
|
||||||
const outerDiv=genuserstrip(user,buttonc);
|
const outerDiv = genuserstrip(user, buttonc);
|
||||||
container.append(outerDiv);
|
container.append(outerDiv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
blocked.onclick=genBlocked;
|
blocked.onclick = genBlocked;
|
||||||
channelTopic.append(blocked);
|
channelTopic.append(blocked);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const add=document.createElement("button");
|
const add = document.createElement("button");
|
||||||
add.textContent=I18n.getTranslation("friends.addfriend");
|
add.textContent = I18n.getTranslation("friends.addfriend");
|
||||||
add.onclick=()=>{
|
add.onclick = () => {
|
||||||
this.localuser.relationshipsUpdate=()=>{};
|
this.localuser.relationshipsUpdate = () => {};
|
||||||
container.innerHTML="";
|
container.innerHTML = "";
|
||||||
const float=new Float("");
|
const float = new Float("");
|
||||||
const options=float.options;
|
const options = float.options;
|
||||||
const form=options.addForm("",(e:any)=>{
|
const form = options.addForm(
|
||||||
|
"",
|
||||||
|
(e: any) => {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
if(e.code===404){
|
if (e.code === 404) {
|
||||||
throw new FormError(text,I18n.getTranslation("friends.notfound"));
|
throw new FormError(text, I18n.getTranslation("friends.notfound"));
|
||||||
}else if(e.code===400){
|
} else if (e.code === 400) {
|
||||||
throw new FormError(text,e.message.split("Error: ")[1]);
|
throw new FormError(text, e.message.split("Error: ")[1]);
|
||||||
}else{
|
} else {
|
||||||
const box=text.input.deref();
|
const box = text.input.deref();
|
||||||
if(!box)return;
|
if (!box) return;
|
||||||
box.value="";
|
box.value = "";
|
||||||
}
|
}
|
||||||
},{
|
},
|
||||||
method:"POST",
|
{
|
||||||
fetchURL:this.info.api+"/users/@me/relationships",
|
method: "POST",
|
||||||
headers:this.headers
|
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("#");
|
const text = form.addTextInput(I18n.getTranslation("friends.addfriendpromt"), "username");
|
||||||
obj.username=username;
|
form.addPreprocessor((obj: any) => {
|
||||||
obj.discriminator=discriminator;
|
const [username, discriminator] = obj.username.split("#");
|
||||||
if(!discriminator){
|
obj.username = username;
|
||||||
throw new FormError(text,I18n.getTranslation("friends.discnotfound"));
|
obj.discriminator = discriminator;
|
||||||
|
if (!discriminator) {
|
||||||
|
throw new FormError(text, I18n.getTranslation("friends.discnotfound"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
container.append(float.generateHTML());
|
container.append(float.generateHTML());
|
||||||
}
|
};
|
||||||
channelTopic.append(add);
|
channelTopic.append(add);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get mentions(){
|
get mentions() {
|
||||||
let mentions=0;
|
let mentions = 0;
|
||||||
for(const thing of this.localuser.inrelation){
|
for (const thing of this.localuser.inrelation) {
|
||||||
if(thing.relationshipType===3){
|
if (thing.relationshipType === 3) {
|
||||||
mentions+=1;
|
mentions += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mentions;
|
return mentions;
|
||||||
}
|
}
|
||||||
giveMember(_member: memberjson){
|
giveMember(_member: memberjson) {
|
||||||
throw new Error("not a real guild, can't give member object");
|
throw new Error("not a real guild, can't give member object");
|
||||||
}
|
}
|
||||||
getRole(/* ID: string */){
|
getRole(/* ID: string */) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
hasRole(/* r: string */){
|
hasRole(/* r: string */) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
isAdmin(){
|
isAdmin() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
unreaddms(){
|
unreaddms() {
|
||||||
for(const thing of this.channels){
|
for (const thing of this.channels) {
|
||||||
(thing as Group).unreads();
|
(thing as Group).unreads();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -335,34 +339,46 @@ dmPermissions.setPermission("STREAM", 1);
|
||||||
dmPermissions.setPermission("USE_VAD", 1);
|
dmPermissions.setPermission("USE_VAD", 1);
|
||||||
|
|
||||||
// @ts-ignore I need to look into this lol
|
// @ts-ignore I need to look into this lol
|
||||||
class Group extends Channel{
|
class Group extends Channel {
|
||||||
user: User;
|
user: User;
|
||||||
static contextmenu = new Contextmenu<Group, undefined>("channel menu");
|
static contextmenu = new Contextmenu<Group, undefined>("channel menu");
|
||||||
static setupcontextmenu(){
|
static setupcontextmenu() {
|
||||||
this.contextmenu.addbutton(()=>I18n.getTranslation("DMs.copyId"), function(this: Group){
|
this.contextmenu.addbutton(
|
||||||
|
() => I18n.getTranslation("DMs.copyId"),
|
||||||
|
function (this: Group) {
|
||||||
navigator.clipboard.writeText(this.id);
|
navigator.clipboard.writeText(this.id);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
this.contextmenu.addbutton(()=>I18n.getTranslation("DMs.markRead"), function(this: Group){
|
this.contextmenu.addbutton(
|
||||||
|
() => I18n.getTranslation("DMs.markRead"),
|
||||||
|
function (this: Group) {
|
||||||
this.readbottom();
|
this.readbottom();
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
this.contextmenu.addbutton(()=>I18n.getTranslation("DMs.close"), function(this: Group){
|
this.contextmenu.addbutton(
|
||||||
|
() => I18n.getTranslation("DMs.close"),
|
||||||
|
function (this: Group) {
|
||||||
this.deleteChannel();
|
this.deleteChannel();
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
this.contextmenu.addbutton(()=>I18n.getTranslation("user.copyId"), function(){
|
this.contextmenu.addbutton(
|
||||||
|
() => I18n.getTranslation("user.copyId"),
|
||||||
|
function () {
|
||||||
navigator.clipboard.writeText(this.user.id);
|
navigator.clipboard.writeText(this.user.id);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
constructor(json: dirrectjson, owner: Direct){
|
constructor(json: dirrectjson, owner: Direct) {
|
||||||
super(-1, owner, json.id);
|
super(-1, owner, json.id);
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.headers = this.guild.headers;
|
this.headers = this.guild.headers;
|
||||||
this.name = json.recipients[0]?.username;
|
this.name = json.recipients[0]?.username;
|
||||||
if(json.recipients[0]){
|
if (json.recipients[0]) {
|
||||||
this.user = new User(json.recipients[0], this.localuser);
|
this.user = new User(json.recipients[0], this.localuser);
|
||||||
}else{
|
} else {
|
||||||
this.user = this.localuser.user;
|
this.user = this.localuser.user;
|
||||||
}
|
}
|
||||||
this.name ??= this.localuser.user.username;
|
this.name ??= this.localuser.user.username;
|
||||||
|
@ -376,26 +392,26 @@ class Group extends Channel{
|
||||||
this.setUpInfiniteScroller();
|
this.setUpInfiniteScroller();
|
||||||
this.updatePosition();
|
this.updatePosition();
|
||||||
}
|
}
|
||||||
updatePosition(){
|
updatePosition() {
|
||||||
if(this.lastmessageid){
|
if (this.lastmessageid) {
|
||||||
this.position = SnowFlake.stringToUnixTime(this.lastmessageid);
|
this.position = SnowFlake.stringToUnixTime(this.lastmessageid);
|
||||||
}else{
|
} else {
|
||||||
this.position = 0;
|
this.position = 0;
|
||||||
}
|
}
|
||||||
this.position = -Math.max(this.position, this.getUnixTime());
|
this.position = -Math.max(this.position, this.getUnixTime());
|
||||||
}
|
}
|
||||||
createguildHTML(){
|
createguildHTML() {
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
Group.contextmenu.bindContextmenu(div, this,undefined);
|
Group.contextmenu.bindContextmenu(div, this, undefined);
|
||||||
this.html = new WeakRef(div);
|
this.html = new WeakRef(div);
|
||||||
div.classList.add("flexltr","liststyle");
|
div.classList.add("flexltr", "liststyle");
|
||||||
const myhtml = document.createElement("span");
|
const myhtml = document.createElement("span");
|
||||||
myhtml.classList.add("ellipsis");
|
myhtml.classList.add("ellipsis");
|
||||||
myhtml.textContent = this.name;
|
myhtml.textContent = this.name;
|
||||||
div.appendChild(this.user.buildpfp());
|
div.appendChild(this.user.buildpfp());
|
||||||
div.appendChild(myhtml);
|
div.appendChild(myhtml);
|
||||||
(div as any).myinfo = this;
|
(div as any).myinfo = this;
|
||||||
div.onclick = _=>{
|
div.onclick = (_) => {
|
||||||
this.getHTML();
|
this.getHTML();
|
||||||
const toggle = document.getElementById("maintoggle") as HTMLInputElement;
|
const toggle = document.getElementById("maintoggle") as HTMLInputElement;
|
||||||
toggle.checked = true;
|
toggle.checked = true;
|
||||||
|
@ -403,56 +419,55 @@ class Group extends Channel{
|
||||||
|
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
async getHTML(addstate=true){
|
async getHTML(addstate = true) {
|
||||||
const id = ++Channel.genid;
|
const id = ++Channel.genid;
|
||||||
if(this.localuser.channelfocus){
|
if (this.localuser.channelfocus) {
|
||||||
this.localuser.channelfocus.infinite.delete();
|
this.localuser.channelfocus.infinite.delete();
|
||||||
}
|
}
|
||||||
if(this.guild !== this.localuser.lookingguild){
|
if (this.guild !== this.localuser.lookingguild) {
|
||||||
this.guild.loadGuild();
|
this.guild.loadGuild();
|
||||||
}
|
}
|
||||||
this.guild.prevchannel = this;
|
this.guild.prevchannel = this;
|
||||||
this.localuser.channelfocus = this;
|
this.localuser.channelfocus = this;
|
||||||
const prom = this.infinite.delete();
|
const prom = this.infinite.delete();
|
||||||
if(addstate){
|
if (addstate) {
|
||||||
history.pushState([this.guild_id,this.id], "", "/channels/" + this.guild_id + "/" + this.id);
|
history.pushState([this.guild_id, this.id], "", "/channels/" + this.guild_id + "/" + this.id);
|
||||||
}
|
}
|
||||||
this.localuser.pageTitle("@" + this.name);
|
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;
|
const loading = document.getElementById("loadingdiv") as HTMLDivElement;
|
||||||
Channel.regenLoadingMessages();
|
Channel.regenLoadingMessages();
|
||||||
|
|
||||||
loading.classList.add("loading");
|
loading.classList.add("loading");
|
||||||
this.rendertyping();
|
this.rendertyping();
|
||||||
(document.getElementById("typebox") as HTMLDivElement).contentEditable ="" + true;
|
(document.getElementById("typebox") as HTMLDivElement).contentEditable = "" + true;
|
||||||
(document.getElementById("upload") as HTMLElement).style.visibility="visible";
|
(document.getElementById("upload") as HTMLElement).style.visibility = "visible";
|
||||||
(document.getElementById("typediv") as HTMLElement).style.visibility="visible";
|
(document.getElementById("typediv") as HTMLElement).style.visibility = "visible";
|
||||||
(document.getElementById("typebox") as HTMLDivElement).focus();
|
(document.getElementById("typebox") as HTMLDivElement).focus();
|
||||||
await this.putmessages();
|
await this.putmessages();
|
||||||
await prom;
|
await prom;
|
||||||
this.localuser.getSidePannel();
|
this.localuser.getSidePannel();
|
||||||
if(id !== Channel.genid){
|
if (id !== Channel.genid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.buildmessages();
|
this.buildmessages();
|
||||||
|
|
||||||
}
|
}
|
||||||
messageCreate(messagep: { d: messagejson }){
|
messageCreate(messagep: {d: messagejson}) {
|
||||||
this.mentions++;
|
this.mentions++;
|
||||||
const messagez = new Message(messagep.d, this);
|
const messagez = new Message(messagep.d, this);
|
||||||
if(this.lastmessageid){
|
if (this.lastmessageid) {
|
||||||
this.idToNext.set(this.lastmessageid, messagez.id);
|
this.idToNext.set(this.lastmessageid, messagez.id);
|
||||||
this.idToPrev.set(messagez.id, this.lastmessageid);
|
this.idToPrev.set(messagez.id, this.lastmessageid);
|
||||||
}
|
}
|
||||||
this.lastmessageid = messagez.id;
|
this.lastmessageid = messagez.id;
|
||||||
if(messagez.author === this.localuser.user){
|
if (messagez.author === this.localuser.user) {
|
||||||
this.lastreadmessageid = messagez.id;
|
this.lastreadmessageid = messagez.id;
|
||||||
if(this.myhtml){
|
if (this.myhtml) {
|
||||||
this.myhtml.classList.remove("cunread");
|
this.myhtml.classList.remove("cunread");
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
if(this.myhtml){
|
if (this.myhtml) {
|
||||||
this.myhtml.classList.add("cunread");
|
this.myhtml.classList.add("cunread");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -460,55 +475,55 @@ class Group extends Channel{
|
||||||
this.updatePosition();
|
this.updatePosition();
|
||||||
this.infinite.addedBottom();
|
this.infinite.addedBottom();
|
||||||
this.guild.sortchannels();
|
this.guild.sortchannels();
|
||||||
if(this.myhtml){
|
if (this.myhtml) {
|
||||||
const parrent = this.myhtml.parentElement as HTMLElement;
|
const parrent = this.myhtml.parentElement as HTMLElement;
|
||||||
parrent.prepend(this.myhtml);
|
parrent.prepend(this.myhtml);
|
||||||
}
|
}
|
||||||
if(this === this.localuser.channelfocus){
|
if (this === this.localuser.channelfocus) {
|
||||||
if(!this.infinitefocus){
|
if (!this.infinitefocus) {
|
||||||
this.tryfocusinfinate();
|
this.tryfocusinfinate();
|
||||||
}
|
}
|
||||||
this.infinite.addedBottom();
|
this.infinite.addedBottom();
|
||||||
}
|
}
|
||||||
this.unreads();
|
this.unreads();
|
||||||
if(messagez.author === this.localuser.user){
|
if (messagez.author === this.localuser.user) {
|
||||||
this.mentions=0;
|
this.mentions = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(this.localuser.lookingguild?.prevchannel === this && document.hasFocus()){
|
if (this.localuser.lookingguild?.prevchannel === this && document.hasFocus()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(this.notification === "all"){
|
if (this.notification === "all") {
|
||||||
this.notify(messagez);
|
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);
|
this.notify(messagez);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
notititle(message: Message){
|
notititle(message: Message) {
|
||||||
return message.author.username;
|
return message.author.username;
|
||||||
}
|
}
|
||||||
readbottom(){
|
readbottom() {
|
||||||
super.readbottom();
|
super.readbottom();
|
||||||
this.unreads();
|
this.unreads();
|
||||||
}
|
}
|
||||||
all: WeakRef<HTMLElement> = new WeakRef(document.createElement("div"));
|
all: WeakRef<HTMLElement> = new WeakRef(document.createElement("div"));
|
||||||
noti: WeakRef<HTMLElement> = new WeakRef(document.createElement("div"));
|
noti: WeakRef<HTMLElement> = new WeakRef(document.createElement("div"));
|
||||||
del(){
|
del() {
|
||||||
const all = this.all.deref();
|
const all = this.all.deref();
|
||||||
if(all){
|
if (all) {
|
||||||
all.remove();
|
all.remove();
|
||||||
}
|
}
|
||||||
if(this.myhtml){
|
if (this.myhtml) {
|
||||||
this.myhtml.remove();
|
this.myhtml.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unreads(){
|
unreads() {
|
||||||
const sentdms = document.getElementById("sentdms") as HTMLDivElement; //Need to change sometime
|
const sentdms = document.getElementById("sentdms") as HTMLDivElement; //Need to change sometime
|
||||||
const current = this.all.deref();
|
const current = this.all.deref();
|
||||||
if(this.hasunreads){
|
if (this.hasunreads) {
|
||||||
{
|
{
|
||||||
const noti = this.noti.deref();
|
const noti = this.noti.deref();
|
||||||
if(noti){
|
if (noti) {
|
||||||
noti.textContent = this.mentions + "";
|
noti.textContent = this.mentions + "";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -525,24 +540,24 @@ class Group extends Channel{
|
||||||
buildpfp.classList.add("mentioned");
|
buildpfp.classList.add("mentioned");
|
||||||
div.append(buildpfp);
|
div.append(buildpfp);
|
||||||
sentdms.append(div);
|
sentdms.append(div);
|
||||||
div.onclick = _=>{
|
div.onclick = (_) => {
|
||||||
this.guild.loadGuild();
|
this.guild.loadGuild();
|
||||||
this.getHTML();
|
this.getHTML();
|
||||||
const toggle = document.getElementById("maintoggle") as HTMLInputElement;
|
const toggle = document.getElementById("maintoggle") as HTMLInputElement;
|
||||||
toggle.checked = true;
|
toggle.checked = true;
|
||||||
};
|
};
|
||||||
}else if(current){
|
} else if (current) {
|
||||||
current.remove();
|
current.remove();
|
||||||
}else{
|
} else {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isAdmin(): boolean{
|
isAdmin(): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
hasPermission(name: string): boolean{
|
hasPermission(name: string): boolean {
|
||||||
return dmPermissions.hasPermission(name);
|
return dmPermissions.hasPermission(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export{ Direct, Group };
|
export {Direct, Group};
|
||||||
|
|
||||||
Group.setupcontextmenu()
|
Group.setupcontextmenu();
|
||||||
|
|
|
@ -1,37 +1,37 @@
|
||||||
class ImagesDisplay{
|
class ImagesDisplay {
|
||||||
images:string[];
|
images: string[];
|
||||||
index=0;
|
index = 0;
|
||||||
constructor(srcs:string[],index=0){
|
constructor(srcs: string[], index = 0) {
|
||||||
this.images=srcs;
|
this.images = srcs;
|
||||||
this.index=index;
|
this.index = index;
|
||||||
}
|
}
|
||||||
weakbg=new WeakRef<HTMLElement>(document.createElement("div"));
|
weakbg = new WeakRef<HTMLElement>(document.createElement("div"));
|
||||||
get background():HTMLElement|undefined{
|
get background(): HTMLElement | undefined {
|
||||||
return this.weakbg.deref();
|
return this.weakbg.deref();
|
||||||
}
|
}
|
||||||
set background(e:HTMLElement){
|
set background(e: HTMLElement) {
|
||||||
this.weakbg=new WeakRef(e);
|
this.weakbg = new WeakRef(e);
|
||||||
}
|
}
|
||||||
makeHTML():HTMLElement{
|
makeHTML(): HTMLElement {
|
||||||
//TODO this should be able to display more than one image at a time lol
|
//TODO this should be able to display more than one image at a time lol
|
||||||
const image= document.createElement("img");
|
const image = document.createElement("img");
|
||||||
image.src=this.images[this.index];
|
image.src = this.images[this.index];
|
||||||
image.classList.add("imgfit","centeritem");
|
image.classList.add("imgfit", "centeritem");
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
show(){
|
show() {
|
||||||
this.background = document.createElement("div");
|
this.background = document.createElement("div");
|
||||||
this.background.classList.add("background");
|
this.background.classList.add("background");
|
||||||
this.background.appendChild(this.makeHTML());
|
this.background.appendChild(this.makeHTML());
|
||||||
this.background.onclick = _=>{
|
this.background.onclick = (_) => {
|
||||||
this.hide();
|
this.hide();
|
||||||
};
|
};
|
||||||
document.body.append(this.background);
|
document.body.append(this.background);
|
||||||
}
|
}
|
||||||
hide(){
|
hide() {
|
||||||
if(this.background){
|
if (this.background) {
|
||||||
this.background.remove();
|
this.background.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export{ImagesDisplay}
|
export {ImagesDisplay};
|
||||||
|
|
|
@ -1,94 +1,85 @@
|
||||||
import{ Message }from"./message.js";
|
import {Message} from "./message.js";
|
||||||
import{ MarkDown }from"./markdown.js";
|
import {MarkDown} from "./markdown.js";
|
||||||
import{ embedjson, invitejson }from"./jsontypes.js";
|
import {embedjson, invitejson} from "./jsontypes.js";
|
||||||
import{ getapiurls, getInstances }from"./utils/utils.js";
|
import {getapiurls, getInstances} from "./utils/utils.js";
|
||||||
import{ Guild }from"./guild.js";
|
import {Guild} from "./guild.js";
|
||||||
import { I18n } from "./i18n.js";
|
import {I18n} from "./i18n.js";
|
||||||
import { ImagesDisplay } from "./disimg.js";
|
import {ImagesDisplay} from "./disimg.js";
|
||||||
|
|
||||||
class Embed{
|
class Embed {
|
||||||
type: string;
|
type: string;
|
||||||
owner: Message;
|
owner: Message;
|
||||||
json: embedjson;
|
json: embedjson;
|
||||||
constructor(json: embedjson, owner: Message){
|
constructor(json: embedjson, owner: Message) {
|
||||||
this.type = this.getType(json);
|
this.type = this.getType(json);
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.json = json;
|
this.json = json;
|
||||||
}
|
}
|
||||||
getType(json: embedjson){
|
getType(json: embedjson) {
|
||||||
const instances = getInstances();
|
const instances = getInstances();
|
||||||
if(
|
if (instances && json.type === "link" && json.url && URL.canParse(json.url)) {
|
||||||
instances &&
|
|
||||||
json.type === "link" &&
|
|
||||||
json.url &&
|
|
||||||
URL.canParse(json.url)
|
|
||||||
){
|
|
||||||
const Url = new URL(json.url);
|
const Url = new URL(json.url);
|
||||||
for(const instance of instances){
|
for (const instance of instances) {
|
||||||
if(instance.url && URL.canParse(instance.url)){
|
if (instance.url && URL.canParse(instance.url)) {
|
||||||
const IUrl = new URL(instance.url);
|
const IUrl = new URL(instance.url);
|
||||||
const params = new URLSearchParams(Url.search);
|
const params = new URLSearchParams(Url.search);
|
||||||
let host: string;
|
let host: string;
|
||||||
if(params.has("instance")){
|
if (params.has("instance")) {
|
||||||
const url = params.get("instance") as string;
|
const url = params.get("instance") as string;
|
||||||
if(URL.canParse(url)){
|
if (URL.canParse(url)) {
|
||||||
host = new URL(url).host;
|
host = new URL(url).host;
|
||||||
}else{
|
} else {
|
||||||
host = Url.host;
|
host = Url.host;
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
host = Url.host;
|
host = Url.host;
|
||||||
}
|
}
|
||||||
if(IUrl.host === host){
|
if (IUrl.host === host) {
|
||||||
const code =
|
const code = Url.pathname.split("/")[Url.pathname.split("/").length - 1];
|
||||||
Url.pathname.split("/")[Url.pathname.split("/").length - 1];
|
|
||||||
json.invite = {
|
json.invite = {
|
||||||
url: instance.url,
|
url: instance.url,
|
||||||
code,
|
code,
|
||||||
};
|
};
|
||||||
return"invite";
|
return "invite";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return json.type || "rich";
|
return json.type || "rich";
|
||||||
}
|
}
|
||||||
generateHTML(){
|
generateHTML() {
|
||||||
switch(this.type){
|
switch (this.type) {
|
||||||
case"rich":
|
case "rich":
|
||||||
return this.generateRich();
|
return this.generateRich();
|
||||||
case"image":
|
case "image":
|
||||||
return this.generateImage();
|
return this.generateImage();
|
||||||
case"invite":
|
case "invite":
|
||||||
return this.generateInvite();
|
return this.generateInvite();
|
||||||
case"link":
|
case "link":
|
||||||
return this.generateLink();
|
return this.generateLink();
|
||||||
case"video":
|
case "video":
|
||||||
case"article":
|
case "article":
|
||||||
return this.generateArticle();
|
return this.generateArticle();
|
||||||
default:
|
default:
|
||||||
console.warn(
|
console.warn(`unsupported embed type ${this.type}, please add support dev :3`, this.json);
|
||||||
`unsupported embed type ${this.type}, please add support dev :3`,
|
|
||||||
this.json
|
|
||||||
);
|
|
||||||
return document.createElement("div"); //prevent errors by giving blank div
|
return document.createElement("div"); //prevent errors by giving blank div
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get message(){
|
get message() {
|
||||||
return this.owner;
|
return this.owner;
|
||||||
}
|
}
|
||||||
get channel(){
|
get channel() {
|
||||||
return this.message.channel;
|
return this.message.channel;
|
||||||
}
|
}
|
||||||
get guild(){
|
get guild() {
|
||||||
return this.channel.guild;
|
return this.channel.guild;
|
||||||
}
|
}
|
||||||
get localuser(){
|
get localuser() {
|
||||||
return this.guild.localuser;
|
return this.guild.localuser;
|
||||||
}
|
}
|
||||||
generateRich(){
|
generateRich() {
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
if(this.json.color){
|
if (this.json.color) {
|
||||||
div.style.backgroundColor = "#" + this.json.color.toString(16);
|
div.style.backgroundColor = "#" + this.json.color.toString(16);
|
||||||
}
|
}
|
||||||
div.classList.add("embed-color");
|
div.classList.add("embed-color");
|
||||||
|
@ -97,9 +88,9 @@ Url.pathname.split("/")[Url.pathname.split("/").length - 1];
|
||||||
embed.classList.add("embed");
|
embed.classList.add("embed");
|
||||||
div.append(embed);
|
div.append(embed);
|
||||||
|
|
||||||
if(this.json.author){
|
if (this.json.author) {
|
||||||
const authorline = document.createElement("div");
|
const authorline = document.createElement("div");
|
||||||
if(this.json.author.icon_url){
|
if (this.json.author.icon_url) {
|
||||||
const img = document.createElement("img");
|
const img = document.createElement("img");
|
||||||
img.classList.add("embedimg");
|
img.classList.add("embedimg");
|
||||||
img.src = this.json.author.icon_url;
|
img.src = this.json.author.icon_url;
|
||||||
|
@ -107,31 +98,31 @@ Url.pathname.split("/")[Url.pathname.split("/").length - 1];
|
||||||
}
|
}
|
||||||
const a = document.createElement("a");
|
const a = document.createElement("a");
|
||||||
a.textContent = this.json.author.name as string;
|
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);
|
MarkDown.safeLink(a, this.json.author.url);
|
||||||
}
|
}
|
||||||
a.classList.add("username");
|
a.classList.add("username");
|
||||||
authorline.append(a);
|
authorline.append(a);
|
||||||
embed.append(authorline);
|
embed.append(authorline);
|
||||||
}
|
}
|
||||||
if(this.json.title){
|
if (this.json.title) {
|
||||||
const title = document.createElement("a");
|
const title = document.createElement("a");
|
||||||
title.append(new MarkDown(this.json.title, this.channel).makeHTML());
|
title.append(new MarkDown(this.json.title, this.channel).makeHTML());
|
||||||
if(this.json.url){
|
if (this.json.url) {
|
||||||
MarkDown.safeLink(title, this.json.url);
|
MarkDown.safeLink(title, this.json.url);
|
||||||
}
|
}
|
||||||
title.classList.add("embedtitle");
|
title.classList.add("embedtitle");
|
||||||
embed.append(title);
|
embed.append(title);
|
||||||
}
|
}
|
||||||
if(this.json.description){
|
if (this.json.description) {
|
||||||
const p = document.createElement("p");
|
const p = document.createElement("p");
|
||||||
p.append(new MarkDown(this.json.description, this.channel).makeHTML());
|
p.append(new MarkDown(this.json.description, this.channel).makeHTML());
|
||||||
embed.append(p);
|
embed.append(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
embed.append(document.createElement("br"));
|
embed.append(document.createElement("br"));
|
||||||
if(this.json.fields){
|
if (this.json.fields) {
|
||||||
for(const thing of this.json.fields){
|
for (const thing of this.json.fields) {
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
const b = document.createElement("b");
|
const b = document.createElement("b");
|
||||||
b.textContent = thing.name;
|
b.textContent = thing.name;
|
||||||
|
@ -141,31 +132,31 @@ Url.pathname.split("/")[Url.pathname.split("/").length - 1];
|
||||||
p.classList.add("embedp");
|
p.classList.add("embedp");
|
||||||
div.append(p);
|
div.append(p);
|
||||||
|
|
||||||
if(thing.inline){
|
if (thing.inline) {
|
||||||
div.classList.add("inline");
|
div.classList.add("inline");
|
||||||
}
|
}
|
||||||
embed.append(div);
|
embed.append(div);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(this.json.footer || this.json.timestamp){
|
if (this.json.footer || this.json.timestamp) {
|
||||||
const footer = document.createElement("div");
|
const footer = document.createElement("div");
|
||||||
if(this.json?.footer?.icon_url){
|
if (this.json?.footer?.icon_url) {
|
||||||
const img = document.createElement("img");
|
const img = document.createElement("img");
|
||||||
img.src = this.json.footer.icon_url;
|
img.src = this.json.footer.icon_url;
|
||||||
img.classList.add("embedicon");
|
img.classList.add("embedicon");
|
||||||
footer.append(img);
|
footer.append(img);
|
||||||
}
|
}
|
||||||
if(this.json?.footer?.text){
|
if (this.json?.footer?.text) {
|
||||||
const span = document.createElement("span");
|
const span = document.createElement("span");
|
||||||
span.textContent = this.json.footer.text;
|
span.textContent = this.json.footer.text;
|
||||||
footer.append(span);
|
footer.append(span);
|
||||||
}
|
}
|
||||||
if(this.json?.footer && this.json?.timestamp){
|
if (this.json?.footer && this.json?.timestamp) {
|
||||||
const span = document.createElement("span");
|
const span = document.createElement("span");
|
||||||
span.textContent = " • ";
|
span.textContent = " • ";
|
||||||
footer.append(span);
|
footer.append(span);
|
||||||
}
|
}
|
||||||
if(this.json?.timestamp){
|
if (this.json?.timestamp) {
|
||||||
const span = document.createElement("span");
|
const span = document.createElement("span");
|
||||||
span.textContent = new Date(this.json.timestamp).toLocaleString();
|
span.textContent = new Date(this.json.timestamp).toLocaleString();
|
||||||
footer.append(span);
|
footer.append(span);
|
||||||
|
@ -174,15 +165,15 @@ Url.pathname.split("/")[Url.pathname.split("/").length - 1];
|
||||||
}
|
}
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
generateImage(){
|
generateImage() {
|
||||||
const img = document.createElement("img");
|
const img = document.createElement("img");
|
||||||
img.classList.add("messageimg");
|
img.classList.add("messageimg");
|
||||||
img.onclick = function(){
|
img.onclick = function () {
|
||||||
const full = new ImagesDisplay([img.src]);
|
const full = new ImagesDisplay([img.src]);
|
||||||
full.show();
|
full.show();
|
||||||
};
|
};
|
||||||
img.src = this.json.thumbnail.proxy_url;
|
img.src = this.json.thumbnail.proxy_url;
|
||||||
if(this.json.thumbnail.width){
|
if (this.json.thumbnail.width) {
|
||||||
let scale = 1;
|
let scale = 1;
|
||||||
const max = 96 * 3;
|
const max = 96 * 3;
|
||||||
scale = Math.max(scale, this.json.thumbnail.width / max);
|
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");
|
console.log(this.json, "Image fix");
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
generateLink(){
|
generateLink() {
|
||||||
const table = document.createElement("table");
|
const table = document.createElement("table");
|
||||||
table.classList.add("embed", "linkembed");
|
table.classList.add("embed", "linkembed");
|
||||||
const trtop = document.createElement("tr");
|
const trtop = document.createElement("tr");
|
||||||
table.append(trtop);
|
table.append(trtop);
|
||||||
if(this.json.url && this.json.title){
|
if (this.json.url && this.json.title) {
|
||||||
const td = document.createElement("td");
|
const td = document.createElement("td");
|
||||||
const a = document.createElement("a");
|
const a = document.createElement("a");
|
||||||
MarkDown.safeLink(a, this.json.url);
|
MarkDown.safeLink(a, this.json.url);
|
||||||
|
@ -211,9 +202,9 @@ Url.pathname.split("/")[Url.pathname.split("/").length - 1];
|
||||||
{
|
{
|
||||||
const td = document.createElement("td");
|
const td = document.createElement("td");
|
||||||
const img = document.createElement("img");
|
const img = document.createElement("img");
|
||||||
if(this.json.thumbnail){
|
if (this.json.thumbnail) {
|
||||||
img.classList.add("embedimg");
|
img.classList.add("embedimg");
|
||||||
img.onclick = function(){
|
img.onclick = function () {
|
||||||
const full = new ImagesDisplay([img.src]);
|
const full = new ImagesDisplay([img.src]);
|
||||||
full.show();
|
full.show();
|
||||||
};
|
};
|
||||||
|
@ -224,7 +215,7 @@ Url.pathname.split("/")[Url.pathname.split("/").length - 1];
|
||||||
}
|
}
|
||||||
const bottomtr = document.createElement("tr");
|
const bottomtr = document.createElement("tr");
|
||||||
const td = document.createElement("td");
|
const td = document.createElement("td");
|
||||||
if(this.json.description){
|
if (this.json.description) {
|
||||||
const span = document.createElement("span");
|
const span = document.createElement("span");
|
||||||
span.textContent = this.json.description;
|
span.textContent = this.json.description;
|
||||||
td.append(span);
|
td.append(span);
|
||||||
|
@ -233,59 +224,62 @@ Url.pathname.split("/")[Url.pathname.split("/").length - 1];
|
||||||
table.append(bottomtr);
|
table.append(bottomtr);
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
invcache: [invitejson, { cdn: string; api: string }] | undefined;
|
invcache: [invitejson, {cdn: string; api: string}] | undefined;
|
||||||
generateInvite(){
|
generateInvite() {
|
||||||
if(this.invcache && (!this.json.invite || !this.localuser)){
|
if (this.invcache && (!this.json.invite || !this.localuser)) {
|
||||||
return this.generateLink();
|
return this.generateLink();
|
||||||
}
|
}
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
div.classList.add("embed", "inviteEmbed", "flexttb");
|
div.classList.add("embed", "inviteEmbed", "flexttb");
|
||||||
const json1 = this.json.invite;
|
const json1 = this.json.invite;
|
||||||
(async ()=>{
|
(async () => {
|
||||||
let json: invitejson;
|
let json: invitejson;
|
||||||
let info: { cdn: string; api: string };
|
let info: {cdn: string; api: string};
|
||||||
if(!this.invcache){
|
if (!this.invcache) {
|
||||||
if(!json1){
|
if (!json1) {
|
||||||
div.classList.remove("embed", "inviteEmbed", "flexttb")
|
div.classList.remove("embed", "inviteEmbed", "flexttb");
|
||||||
div.append(this.generateLink());
|
div.append(this.generateLink());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const tempinfo = await getapiurls(json1.url);
|
const tempinfo = await getapiurls(json1.url);
|
||||||
|
|
||||||
if(!tempinfo){
|
if (!tempinfo) {
|
||||||
div.classList.remove("embed", "inviteEmbed", "flexttb")
|
div.classList.remove("embed", "inviteEmbed", "flexttb");
|
||||||
div.append(this.generateLink());
|
div.append(this.generateLink());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
info = tempinfo;
|
info = tempinfo;
|
||||||
const res = await fetch(info.api + "/invites/" + json1.code);
|
const res = await fetch(info.api + "/invites/" + json1.code);
|
||||||
if(!res.ok){
|
if (!res.ok) {
|
||||||
div.classList.remove("embed", "inviteEmbed", "flexttb")
|
div.classList.remove("embed", "inviteEmbed", "flexttb");
|
||||||
div.append(this.generateLink());
|
div.append(this.generateLink());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
json = (await res.json()) as invitejson;
|
json = (await res.json()) as invitejson;
|
||||||
this.invcache = [json, info];
|
this.invcache = [json, info];
|
||||||
}else{
|
} else {
|
||||||
[json, info] = this.invcache;
|
[json, info] = this.invcache;
|
||||||
}
|
}
|
||||||
if(!json){
|
if (!json) {
|
||||||
div.append(this.generateLink());
|
div.append(this.generateLink());
|
||||||
div.classList.remove("embed", "inviteEmbed", "flexttb")
|
div.classList.remove("embed", "inviteEmbed", "flexttb");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(json.guild.banner){
|
if (json.guild.banner) {
|
||||||
const banner = document.createElement("img");
|
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");
|
banner.classList.add("banner");
|
||||||
div.append(banner);
|
div.append(banner);
|
||||||
}
|
}
|
||||||
const guild: invitejson["guild"] & { info?: { cdn: string } } =
|
const guild: invitejson["guild"] & {info?: {cdn: string}} = json.guild;
|
||||||
json.guild;
|
|
||||||
guild.info = info;
|
guild.info = info;
|
||||||
const icon = Guild.generateGuildIcon(
|
const icon = Guild.generateGuildIcon(guild as invitejson["guild"] & {info: {cdn: string}});
|
||||||
guild as invitejson["guild"] & { info: { cdn: string } }
|
|
||||||
);
|
|
||||||
const iconrow = document.createElement("div");
|
const iconrow = document.createElement("div");
|
||||||
iconrow.classList.add("flexltr");
|
iconrow.classList.add("flexltr");
|
||||||
iconrow.append(icon);
|
iconrow.append(icon);
|
||||||
|
@ -305,30 +299,30 @@ guild as invitejson["guild"] & { info: { cdn: string } }
|
||||||
|
|
||||||
div.append(iconrow);
|
div.append(iconrow);
|
||||||
const h2 = document.createElement("h2");
|
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);
|
div.append(h2);
|
||||||
const button = document.createElement("button");
|
const button = document.createElement("button");
|
||||||
button.textContent = I18n.getTranslation("invite.accept");
|
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.textContent = I18n.getTranslation("invite.alreadyJoined");
|
||||||
button.disabled = true;
|
button.disabled = true;
|
||||||
}
|
}
|
||||||
button.classList.add("acceptinvbutton");
|
button.classList.add("acceptinvbutton");
|
||||||
div.append(button);
|
div.append(button);
|
||||||
button.onclick = _=>{
|
button.onclick = (_) => {
|
||||||
if(this.localuser.info.api.startsWith(info.api)){
|
if (this.localuser.info.api.startsWith(info.api)) {
|
||||||
fetch(this.localuser.info.api + "/invites/" + json.code, {
|
fetch(this.localuser.info.api + "/invites/" + json.code, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: this.localuser.headers,
|
headers: this.localuser.headers,
|
||||||
})
|
})
|
||||||
.then(r=>r.json())
|
.then((r) => r.json())
|
||||||
.then(_=>{
|
.then((_) => {
|
||||||
if(_.message){
|
if (_.message) {
|
||||||
alert(_.message);
|
alert(_.message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}else{
|
} else {
|
||||||
if(this.json.invite){
|
if (this.json.invite) {
|
||||||
const params = new URLSearchParams("");
|
const params = new URLSearchParams("");
|
||||||
params.set("instance", this.json.invite.url);
|
params.set("instance", this.json.invite.url);
|
||||||
const encoded = params.toString();
|
const encoded = params.toString();
|
||||||
|
@ -340,33 +334,33 @@ guild as invitejson["guild"] & { info: { cdn: string } }
|
||||||
})();
|
})();
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
generateArticle(){
|
generateArticle() {
|
||||||
const colordiv = document.createElement("div");
|
const colordiv = document.createElement("div");
|
||||||
colordiv.style.backgroundColor = "#000000";
|
colordiv.style.backgroundColor = "#000000";
|
||||||
colordiv.classList.add("embed-color");
|
colordiv.classList.add("embed-color");
|
||||||
|
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
div.classList.add("embed");
|
div.classList.add("embed");
|
||||||
if(this.json.provider){
|
if (this.json.provider) {
|
||||||
const provider = document.createElement("p");
|
const provider = document.createElement("p");
|
||||||
provider.classList.add("provider");
|
provider.classList.add("provider");
|
||||||
provider.textContent = this.json.provider.name;
|
provider.textContent = this.json.provider.name;
|
||||||
div.append(provider);
|
div.append(provider);
|
||||||
}
|
}
|
||||||
const a = document.createElement("a");
|
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);
|
MarkDown.safeLink(a, this.json.url);
|
||||||
a.textContent = this.json.url;
|
a.textContent = this.json.url;
|
||||||
div.append(a);
|
div.append(a);
|
||||||
}
|
}
|
||||||
if(this.json.description){
|
if (this.json.description) {
|
||||||
const description = document.createElement("p");
|
const description = document.createElement("p");
|
||||||
description.textContent = this.json.description;
|
description.textContent = this.json.description;
|
||||||
div.append(description);
|
div.append(description);
|
||||||
}
|
}
|
||||||
if(this.json.thumbnail){
|
if (this.json.thumbnail) {
|
||||||
const img = document.createElement("img");
|
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;
|
let scale = 1;
|
||||||
const inch = 96;
|
const inch = 96;
|
||||||
scale = Math.max(scale, this.json.thumbnail.width / inch / 4);
|
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.style.height = this.json.thumbnail.height + "px";
|
||||||
}
|
}
|
||||||
img.classList.add("bigembedimg");
|
img.classList.add("bigembedimg");
|
||||||
if(this.json.video){
|
if (this.json.video) {
|
||||||
img.onclick = async ()=>{
|
img.onclick = async () => {
|
||||||
if(this.json.video){
|
if (this.json.video) {
|
||||||
img.remove();
|
img.remove();
|
||||||
const iframe = document.createElement("iframe");
|
const iframe = document.createElement("iframe");
|
||||||
iframe.src = this.json.video.url + "?autoplay=1";
|
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.width = this.json.thumbnail.width + "px";
|
||||||
iframe.style.height = this.json.thumbnail.height + "px";
|
iframe.style.height = this.json.thumbnail.height + "px";
|
||||||
}
|
}
|
||||||
div.append(iframe);
|
div.append(iframe);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}else{
|
} else {
|
||||||
img.onclick = async ()=>{
|
img.onclick = async () => {
|
||||||
const full = new ImagesDisplay([img.src]);
|
const full = new ImagesDisplay([img.src]);
|
||||||
full.show();
|
full.show();
|
||||||
};
|
};
|
||||||
|
@ -403,4 +397,4 @@ guild as invitejson["guild"] & { info: { cdn: string } }
|
||||||
return colordiv;
|
return colordiv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export{ Embed };
|
export {Embed};
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import{ Contextmenu }from"./contextmenu.js";
|
import {Contextmenu} from "./contextmenu.js";
|
||||||
import{ Guild }from"./guild.js";
|
import {Guild} from "./guild.js";
|
||||||
import { emojijson } from "./jsontypes.js";
|
import {emojijson} from "./jsontypes.js";
|
||||||
import{ Localuser }from"./localuser.js";
|
import {Localuser} from "./localuser.js";
|
||||||
import { BinRead } from "./utils/binaryUtils.js";
|
import {BinRead} from "./utils/binaryUtils.js";
|
||||||
|
|
||||||
//I need to recompile the emoji format for translation
|
//I need to recompile the emoji format for translation
|
||||||
class Emoji{
|
class Emoji {
|
||||||
static emojis: {
|
static emojis: {
|
||||||
name: string;
|
name: string;
|
||||||
emojis: {
|
emojis: {
|
||||||
|
@ -15,61 +15,59 @@ class Emoji{
|
||||||
}[];
|
}[];
|
||||||
name: string;
|
name: string;
|
||||||
id?: string;
|
id?: string;
|
||||||
emoji?:string;
|
emoji?: string;
|
||||||
animated: boolean;
|
animated: boolean;
|
||||||
owner: Guild | Localuser;
|
owner: Guild | Localuser;
|
||||||
get guild(){
|
get guild() {
|
||||||
if(this.owner instanceof Guild){
|
if (this.owner instanceof Guild) {
|
||||||
return this.owner;
|
return this.owner;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
get localuser(){
|
get localuser() {
|
||||||
if(this.owner instanceof Guild){
|
if (this.owner instanceof Guild) {
|
||||||
return this.owner.localuser;
|
return this.owner.localuser;
|
||||||
}else{
|
} else {
|
||||||
return this.owner;
|
return this.owner;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get info(){
|
get info() {
|
||||||
return this.owner.info;
|
return this.owner.info;
|
||||||
}
|
}
|
||||||
constructor(
|
constructor(json: emojijson, owner: Guild | Localuser) {
|
||||||
json: emojijson,
|
|
||||||
owner: Guild | Localuser
|
|
||||||
){
|
|
||||||
this.name = json.name;
|
this.name = json.name;
|
||||||
this.id = json.id;
|
this.id = json.id;
|
||||||
this.animated = json.animated||false;
|
this.animated = json.animated || false;
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.emoji=json.emoji;
|
this.emoji = json.emoji;
|
||||||
}
|
}
|
||||||
getHTML(bigemoji: boolean = false){
|
getHTML(bigemoji: boolean = false) {
|
||||||
if(this.id){
|
if (this.id) {
|
||||||
const emojiElem = document.createElement("img");
|
const emojiElem = document.createElement("img");
|
||||||
emojiElem.classList.add("md-emoji");
|
emojiElem.classList.add("md-emoji");
|
||||||
emojiElem.classList.add(bigemoji ? "bigemoji" : "smallemoji");
|
emojiElem.classList.add(bigemoji ? "bigemoji" : "smallemoji");
|
||||||
emojiElem.crossOrigin = "anonymous";
|
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.alt = this.name;
|
||||||
emojiElem.loading = "lazy";
|
emojiElem.loading = "lazy";
|
||||||
return emojiElem;
|
return emojiElem;
|
||||||
}else if(this.emoji){
|
} else if (this.emoji) {
|
||||||
const emojiElem = document.createElement("span");
|
const emojiElem = document.createElement("span");
|
||||||
emojiElem.classList.add("md-emoji");
|
emojiElem.classList.add("md-emoji");
|
||||||
emojiElem.classList.add(bigemoji ? "bigemoji" : "smallemoji");
|
emojiElem.classList.add(bigemoji ? "bigemoji" : "smallemoji");
|
||||||
emojiElem.textContent=this.emoji;
|
emojiElem.textContent = this.emoji;
|
||||||
return emojiElem;
|
return emojiElem;
|
||||||
}else{
|
} else {
|
||||||
throw new Error("This path should *never* be gone down, this means a malformed emoji");
|
throw new Error("This path should *never* be gone down, this means a malformed emoji");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static decodeEmojiList(buffer: ArrayBuffer){
|
static decodeEmojiList(buffer: ArrayBuffer) {
|
||||||
const reader=new BinRead(buffer)
|
const reader = new BinRead(buffer);
|
||||||
const build: { name: string; emojis: { name: string; emoji: string }[] }[] = [];
|
const build: {name: string; emojis: {name: string; emoji: string}[]}[] = [];
|
||||||
let cats = reader.read16();
|
let cats = reader.read16();
|
||||||
|
|
||||||
for(; cats !== 0; cats--){
|
for (; cats !== 0; cats--) {
|
||||||
const name = reader.readString16();
|
const name = reader.readString16();
|
||||||
const emojis: {
|
const emojis: {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -77,7 +75,7 @@ class Emoji{
|
||||||
emoji: string;
|
emoji: string;
|
||||||
}[] = [];
|
}[] = [];
|
||||||
let emojinumber = reader.read16();
|
let emojinumber = reader.read16();
|
||||||
for(; emojinumber !== 0; emojinumber--){
|
for (; emojinumber !== 0; emojinumber--) {
|
||||||
//console.log(emojis);
|
//console.log(emojis);
|
||||||
const name = reader.readString8();
|
const name = reader.readString8();
|
||||||
const len = reader.read8();
|
const len = reader.read8();
|
||||||
|
@ -96,22 +94,18 @@ class Emoji{
|
||||||
}
|
}
|
||||||
this.emojis = build;
|
this.emojis = build;
|
||||||
}
|
}
|
||||||
static grabEmoji(){
|
static grabEmoji() {
|
||||||
fetch("/emoji.bin")
|
fetch("/emoji.bin")
|
||||||
.then(e=>{
|
.then((e) => {
|
||||||
return e.arrayBuffer();
|
return e.arrayBuffer();
|
||||||
})
|
})
|
||||||
.then(e=>{
|
.then((e) => {
|
||||||
Emoji.decodeEmojiList(e);
|
Emoji.decodeEmojiList(e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
static async emojiPicker(
|
static async emojiPicker(x: number, y: number, localuser: Localuser): Promise<Emoji | string> {
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
localuser: Localuser
|
|
||||||
): Promise<Emoji | string>{
|
|
||||||
let res: (r: Emoji | string) => void;
|
let res: (r: Emoji | string) => void;
|
||||||
const promise: Promise<Emoji | string> = new Promise(r=>{
|
const promise: Promise<Emoji | string> = new Promise((r) => {
|
||||||
res = r;
|
res = r;
|
||||||
});
|
});
|
||||||
const menu = document.createElement("div");
|
const menu = document.createElement("div");
|
||||||
|
@ -130,33 +124,39 @@ class Emoji{
|
||||||
|
|
||||||
let isFirst = true;
|
let isFirst = true;
|
||||||
localuser.guilds
|
localuser.guilds
|
||||||
.filter(guild=>guild.id != "@me" && guild.emojis.length > 0)
|
.filter((guild) => guild.id != "@me" && guild.emojis.length > 0)
|
||||||
.forEach(guild=>{
|
.forEach((guild) => {
|
||||||
const select = document.createElement("div");
|
const select = document.createElement("div");
|
||||||
select.classList.add("emojiSelect");
|
select.classList.add("emojiSelect");
|
||||||
|
|
||||||
if(guild.properties.icon){
|
if (guild.properties.icon) {
|
||||||
const img = document.createElement("img");
|
const img = document.createElement("img");
|
||||||
img.classList.add("pfp", "servericon", "emoji-server");
|
img.classList.add("pfp", "servericon", "emoji-server");
|
||||||
img.crossOrigin = "anonymous";
|
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;
|
img.alt = "Server: " + guild.properties.name;
|
||||||
select.appendChild(img);
|
select.appendChild(img);
|
||||||
}else{
|
} else {
|
||||||
const div = document.createElement("span");
|
const div = document.createElement("span");
|
||||||
div.textContent = guild.properties.name
|
div.textContent = guild.properties.name
|
||||||
.replace(/'s /g, " ")
|
.replace(/'s /g, " ")
|
||||||
.replace(/\w+/g, word=>word[0])
|
.replace(/\w+/g, (word) => word[0])
|
||||||
.replace(/\s/g, "");
|
.replace(/\s/g, "");
|
||||||
select.append(div);
|
select.append(div);
|
||||||
}
|
}
|
||||||
|
|
||||||
selection.append(select);
|
selection.append(select);
|
||||||
|
|
||||||
const clickEvent = ()=>{
|
const clickEvent = () => {
|
||||||
title.textContent = guild.properties.name;
|
title.textContent = guild.properties.name;
|
||||||
body.innerHTML = "";
|
body.innerHTML = "";
|
||||||
for(const emojit of guild.emojis){
|
for (const emojit of guild.emojis) {
|
||||||
const emojiElem = document.createElement("div");
|
const emojiElem = document.createElement("div");
|
||||||
emojiElem.classList.add("emojiSelect");
|
emojiElem.classList.add("emojiSelect");
|
||||||
|
|
||||||
|
@ -166,14 +166,14 @@ class Emoji{
|
||||||
name: emojit.name,
|
name: emojit.name,
|
||||||
animated: emojit.animated as boolean,
|
animated: emojit.animated as boolean,
|
||||||
},
|
},
|
||||||
localuser
|
localuser,
|
||||||
);
|
);
|
||||||
emojiElem.append(emojiClass.getHTML());
|
emojiElem.append(emojiClass.getHTML());
|
||||||
body.append(emojiElem);
|
body.append(emojiElem);
|
||||||
|
|
||||||
emojiElem.addEventListener("click", ()=>{
|
emojiElem.addEventListener("click", () => {
|
||||||
res(emojiClass);
|
res(emojiClass);
|
||||||
if(Contextmenu.currentmenu !== ""){
|
if (Contextmenu.currentmenu !== "") {
|
||||||
Contextmenu.currentmenu.remove();
|
Contextmenu.currentmenu.remove();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -181,14 +181,14 @@ class Emoji{
|
||||||
};
|
};
|
||||||
|
|
||||||
select.addEventListener("click", clickEvent);
|
select.addEventListener("click", clickEvent);
|
||||||
if(isFirst){
|
if (isFirst) {
|
||||||
clickEvent();
|
clickEvent();
|
||||||
isFirst = false;
|
isFirst = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(()=>{
|
setTimeout(() => {
|
||||||
if(Contextmenu.currentmenu != ""){
|
if (Contextmenu.currentmenu != "") {
|
||||||
Contextmenu.currentmenu.remove();
|
Contextmenu.currentmenu.remove();
|
||||||
}
|
}
|
||||||
document.body.append(menu);
|
document.body.append(menu);
|
||||||
|
@ -197,29 +197,29 @@ class Emoji{
|
||||||
}, 10);
|
}, 10);
|
||||||
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for(const thing of Emoji.emojis){
|
for (const thing of Emoji.emojis) {
|
||||||
const select = document.createElement("div");
|
const select = document.createElement("div");
|
||||||
select.textContent = thing.emojis[0].emoji;
|
select.textContent = thing.emojis[0].emoji;
|
||||||
select.classList.add("emojiSelect");
|
select.classList.add("emojiSelect");
|
||||||
selection.append(select);
|
selection.append(select);
|
||||||
const clickEvent = ()=>{
|
const clickEvent = () => {
|
||||||
title.textContent = thing.name;
|
title.textContent = thing.name;
|
||||||
body.innerHTML = "";
|
body.innerHTML = "";
|
||||||
for(const emojit of thing.emojis){
|
for (const emojit of thing.emojis) {
|
||||||
const emoji = document.createElement("div");
|
const emoji = document.createElement("div");
|
||||||
emoji.classList.add("emojiSelect");
|
emoji.classList.add("emojiSelect");
|
||||||
emoji.textContent = emojit.emoji;
|
emoji.textContent = emojit.emoji;
|
||||||
body.append(emoji);
|
body.append(emoji);
|
||||||
emoji.onclick = _=>{
|
emoji.onclick = (_) => {
|
||||||
res(emojit.emoji);
|
res(emojit.emoji);
|
||||||
if(Contextmenu.currentmenu !== ""){
|
if (Contextmenu.currentmenu !== "") {
|
||||||
Contextmenu.currentmenu.remove();
|
Contextmenu.currentmenu.remove();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
select.onclick = clickEvent;
|
select.onclick = clickEvent;
|
||||||
if(i === 0){
|
if (i === 0) {
|
||||||
clickEvent();
|
clickEvent();
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
|
@ -228,40 +228,39 @@ class Emoji{
|
||||||
menu.append(body);
|
menu.append(body);
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
static searchEmoji(search:string,localuser:Localuser,results=50):[Emoji,number][]{
|
static searchEmoji(search: string, localuser: Localuser, results = 50): [Emoji, number][] {
|
||||||
const ranked:[emojijson,number][]=[];
|
const ranked: [emojijson, number][] = [];
|
||||||
function similar(json:emojijson){
|
function similar(json: emojijson) {
|
||||||
if(json.name.includes(search)){
|
if (json.name.includes(search)) {
|
||||||
ranked.push([json,search.length/json.name.length]);
|
ranked.push([json, search.length / json.name.length]);
|
||||||
return true;
|
return true;
|
||||||
}else if(json.name.toLowerCase().includes(search.toLowerCase())){
|
} else if (json.name.toLowerCase().includes(search.toLowerCase())) {
|
||||||
ranked.push([json,search.length/json.name.length/1.4]);
|
ranked.push([json, search.length / json.name.length / 1.4]);
|
||||||
return true;
|
return true;
|
||||||
}else{
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(const group of this.emojis){
|
for (const group of this.emojis) {
|
||||||
for(const emoji of group.emojis){
|
for (const emoji of group.emojis) {
|
||||||
similar(emoji)
|
similar(emoji);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const weakGuild=new WeakMap<emojijson,Guild>();
|
const weakGuild = new WeakMap<emojijson, Guild>();
|
||||||
for(const guild of localuser.guilds){
|
for (const guild of localuser.guilds) {
|
||||||
if(guild.id!=="@me"&&guild.emojis.length!==0){
|
if (guild.id !== "@me" && guild.emojis.length !== 0) {
|
||||||
for(const emoji of guild.emojis){
|
for (const emoji of guild.emojis) {
|
||||||
if(similar(emoji)){
|
if (similar(emoji)) {
|
||||||
weakGuild.set(emoji,guild);
|
weakGuild.set(emoji, guild);
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ranked.sort((a,b)=>b[1]-a[1]);
|
}
|
||||||
return ranked.splice(0,results).map(a=>{
|
ranked.sort((a, b) => b[1] - a[1]);
|
||||||
return [new Emoji(a[0],weakGuild.get(a[0])||localuser),a[1]];
|
return ranked.splice(0, results).map((a) => {
|
||||||
|
return [new Emoji(a[0], weakGuild.get(a[0]) || localuser), a[1]];
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Emoji.grabEmoji();
|
Emoji.grabEmoji();
|
||||||
export{ Emoji };
|
export {Emoji};
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import{ Message }from"./message.js";
|
import {Message} from "./message.js";
|
||||||
import{ filejson }from"./jsontypes.js";
|
import {filejson} from "./jsontypes.js";
|
||||||
import { ImagesDisplay } from "./disimg.js";
|
import {ImagesDisplay} from "./disimg.js";
|
||||||
|
|
||||||
class File{
|
class File {
|
||||||
owner: Message | null;
|
owner: Message | null;
|
||||||
id: string;
|
id: string;
|
||||||
filename: string;
|
filename: string;
|
||||||
|
@ -12,7 +12,7 @@ class File{
|
||||||
proxy_url: string | undefined;
|
proxy_url: string | undefined;
|
||||||
url: string;
|
url: string;
|
||||||
size: number;
|
size: number;
|
||||||
constructor(fileJSON: filejson, owner: Message | null){
|
constructor(fileJSON: filejson, owner: Message | null) {
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.id = fileJSON.id;
|
this.id = fileJSON.id;
|
||||||
this.filename = fileJSON.filename;
|
this.filename = fileJSON.filename;
|
||||||
|
@ -24,9 +24,9 @@ class File{
|
||||||
this.content_type = fileJSON.content_type;
|
this.content_type = fileJSON.content_type;
|
||||||
this.size = fileJSON.size;
|
this.size = fileJSON.size;
|
||||||
}
|
}
|
||||||
getHTML(temp: boolean = false): HTMLElement{
|
getHTML(temp: boolean = false): HTMLElement {
|
||||||
const src = this.proxy_url || this.url;
|
const src = this.proxy_url || this.url;
|
||||||
if(this.width && this.height){
|
if (this.width && this.height) {
|
||||||
let scale = 1;
|
let scale = 1;
|
||||||
const max = 96 * 3;
|
const max = 96 * 3;
|
||||||
scale = Math.max(scale, this.width / max);
|
scale = Math.max(scale, this.width / max);
|
||||||
|
@ -34,35 +34,35 @@ class File{
|
||||||
this.width /= scale;
|
this.width /= scale;
|
||||||
this.height /= scale;
|
this.height /= scale;
|
||||||
}
|
}
|
||||||
if(this.content_type.startsWith("image/")){
|
if (this.content_type.startsWith("image/")) {
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
const img = document.createElement("img");
|
const img = document.createElement("img");
|
||||||
img.classList.add("messageimg");
|
img.classList.add("messageimg");
|
||||||
div.classList.add("messageimgdiv");
|
div.classList.add("messageimgdiv");
|
||||||
img.onclick = function(){
|
img.onclick = function () {
|
||||||
const full = new ImagesDisplay([img.src]);
|
const full = new ImagesDisplay([img.src]);
|
||||||
full.show();
|
full.show();
|
||||||
};
|
};
|
||||||
img.src = src;
|
img.src = src;
|
||||||
div.append(img);
|
div.append(img);
|
||||||
if(this.width){
|
if (this.width) {
|
||||||
div.style.width = this.width + "px";
|
div.style.width = this.width + "px";
|
||||||
div.style.height = this.height + "px";
|
div.style.height = this.height + "px";
|
||||||
}
|
}
|
||||||
return div;
|
return div;
|
||||||
}else if(this.content_type.startsWith("video/")){
|
} else if (this.content_type.startsWith("video/")) {
|
||||||
const video = document.createElement("video");
|
const video = document.createElement("video");
|
||||||
const source = document.createElement("source");
|
const source = document.createElement("source");
|
||||||
source.src = src;
|
source.src = src;
|
||||||
video.append(source);
|
video.append(source);
|
||||||
source.type = this.content_type;
|
source.type = this.content_type;
|
||||||
video.controls = !temp;
|
video.controls = !temp;
|
||||||
if(this.width && this.height){
|
if (this.width && this.height) {
|
||||||
video.width = this.width;
|
video.width = this.width;
|
||||||
video.height = this.height;
|
video.height = this.height;
|
||||||
}
|
}
|
||||||
return video;
|
return video;
|
||||||
}else if(this.content_type.startsWith("audio/")){
|
} else if (this.content_type.startsWith("audio/")) {
|
||||||
const audio = document.createElement("audio");
|
const audio = document.createElement("audio");
|
||||||
const source = document.createElement("source");
|
const source = document.createElement("source");
|
||||||
source.src = src;
|
source.src = src;
|
||||||
|
@ -70,11 +70,11 @@ class File{
|
||||||
source.type = this.content_type;
|
source.type = this.content_type;
|
||||||
audio.controls = !temp;
|
audio.controls = !temp;
|
||||||
return audio;
|
return audio;
|
||||||
}else{
|
} else {
|
||||||
return this.createunknown();
|
return this.createunknown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
upHTML(files: Blob[], file: globalThis.File): HTMLElement{
|
upHTML(files: Blob[], file: globalThis.File): HTMLElement {
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
const contained = this.getHTML(true);
|
const contained = this.getHTML(true);
|
||||||
div.classList.add("containedFile");
|
div.classList.add("containedFile");
|
||||||
|
@ -82,9 +82,9 @@ class File{
|
||||||
const controls = document.createElement("div");
|
const controls = document.createElement("div");
|
||||||
const garbage = document.createElement("button");
|
const garbage = document.createElement("button");
|
||||||
const icon = document.createElement("span");
|
const icon = document.createElement("span");
|
||||||
icon.classList.add("svgicon","svg-delete");
|
icon.classList.add("svgicon", "svg-delete");
|
||||||
garbage.append(icon);
|
garbage.append(icon);
|
||||||
garbage.onclick = _=>{
|
garbage.onclick = (_) => {
|
||||||
div.remove();
|
div.remove();
|
||||||
files.splice(files.indexOf(file), 1);
|
files.splice(files.indexOf(file), 1);
|
||||||
};
|
};
|
||||||
|
@ -93,7 +93,7 @@ class File{
|
||||||
controls.append(garbage);
|
controls.append(garbage);
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
static initFromBlob(file: globalThis.File){
|
static initFromBlob(file: globalThis.File) {
|
||||||
return new File(
|
return new File(
|
||||||
{
|
{
|
||||||
filename: file.name,
|
filename: file.name,
|
||||||
|
@ -105,10 +105,10 @@ class File{
|
||||||
url: URL.createObjectURL(file),
|
url: URL.createObjectURL(file),
|
||||||
proxy_url: undefined,
|
proxy_url: undefined,
|
||||||
},
|
},
|
||||||
null
|
null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
createunknown(): HTMLElement{
|
createunknown(): HTMLElement {
|
||||||
console.log("🗎");
|
console.log("🗎");
|
||||||
const src = this.proxy_url || this.url;
|
const src = this.proxy_url || this.url;
|
||||||
const div = document.createElement("table");
|
const div = document.createElement("table");
|
||||||
|
@ -121,12 +121,12 @@ class File{
|
||||||
fileicon.classList.add("fileicon");
|
fileicon.classList.add("fileicon");
|
||||||
fileicon.rowSpan = 2;
|
fileicon.rowSpan = 2;
|
||||||
const nametd = document.createElement("td");
|
const nametd = document.createElement("td");
|
||||||
if(src){
|
if (src) {
|
||||||
const a = document.createElement("a");
|
const a = document.createElement("a");
|
||||||
a.href = src;
|
a.href = src;
|
||||||
a.textContent = this.filename;
|
a.textContent = this.filename;
|
||||||
nametd.append(a);
|
nametd.append(a);
|
||||||
}else{
|
} else {
|
||||||
nametd.textContent = this.filename;
|
nametd.textContent = this.filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,11 +140,13 @@ class File{
|
||||||
div.appendChild(sizetr);
|
div.appendChild(sizetr);
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
static filesizehuman(fsize: number){
|
static filesizehuman(fsize: number) {
|
||||||
const i = fsize == 0 ? 0 : Math.floor(Math.log(fsize) / Math.log(1024));
|
const i = fsize == 0 ? 0 : Math.floor(Math.log(fsize) / Math.log(1024));
|
||||||
return(
|
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
|
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};
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,39 +1,44 @@
|
||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Jank Client</title>
|
<title>Jank Client</title>
|
||||||
<meta content="Jank Client" property="og:title">
|
<meta content="Jank Client" property="og:title" />
|
||||||
<meta content="A spacebar client that has DMs, replying and more" property="og:description">
|
<meta content="A spacebar client that has DMs, replying and more" property="og:description" />
|
||||||
<meta content="/logo.webp" property="og:image">
|
<meta content="/logo.webp" property="og:image" />
|
||||||
<meta content="#4b458c" data-react-helmet="true" name="theme-color">
|
<meta content="#4b458c" data-react-helmet="true" name="theme-color" />
|
||||||
<link href="/style.css" rel="stylesheet">
|
<link href="/style.css" rel="stylesheet" />
|
||||||
<link href="/themes.css" rel="stylesheet" id="lightcss">
|
<link href="/themes.css" rel="stylesheet" id="lightcss" />
|
||||||
<style>body.no-theme{background:#16191b;}@media(prefers-color-scheme:light){body.no-theme{background:#9397bd;}}</style>
|
<style>
|
||||||
|
body.no-theme {
|
||||||
|
background: #16191b;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
body.no-theme {
|
||||||
|
background: #9397bd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="no-theme" style="overflow-y: scroll;">
|
<body class="no-theme" style="overflow-y: scroll">
|
||||||
<div id="titleDiv">
|
<div id="titleDiv">
|
||||||
<img src="/logo.svg" width="40">
|
<img src="/logo.svg" width="40" />
|
||||||
<h1 id="pageTitle">Jank Client</h1>
|
<h1 id="pageTitle">Jank Client</h1>
|
||||||
<a href="/invite/USgYJo?instance=https%3A%2F%2Fspacebar.chat"
|
<a href="/invite/USgYJo?instance=https%3A%2F%2Fspacebar.chat" class="TitleButtons">
|
||||||
class="TitleButtons">
|
|
||||||
Spacebar Guild
|
Spacebar Guild
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/MathMan05/JankClient" class="TitleButtons">
|
<a href="https://github.com/MathMan05/JankClient" class="TitleButtons"> Github </a>
|
||||||
Github
|
<a href="/channels/@me" class="TitleButtons" id="openClient"> Open Client </a>
|
||||||
</a>
|
|
||||||
<a href="/channels/@me" class="TitleButtons" id="openClient">
|
|
||||||
Open Client
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="homePage">
|
<div id="homePage">
|
||||||
|
|
||||||
<h1 class="pagehead" id="welcomeJank">Welcome to Jank Client</h1>
|
<h1 class="pagehead" id="welcomeJank">Welcome to Jank Client</h1>
|
||||||
<div class="pagebox">
|
<div class="pagebox">
|
||||||
<p id="box1title">Jank Client is a Spacebar-compatible client seeking to be as good as it can be with many features including:</p>
|
<p id="box1title">
|
||||||
|
Jank Client is a Spacebar-compatible client seeking to be as good as it can be with many
|
||||||
|
features including:
|
||||||
|
</p>
|
||||||
<ul id="box1Items">
|
<ul id="box1Items">
|
||||||
<li>Direct Messaging</li>
|
<li>Direct Messaging</li>
|
||||||
<li>Reactions support</li>
|
<li>Reactions support</li>
|
||||||
|
@ -47,18 +52,18 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="pagebox">
|
<div class="pagebox">
|
||||||
<h2 id="compatableInstances">Spacebar-Compatible Instances:</h2>
|
<h2 id="compatableInstances">Spacebar-Compatible Instances:</h2>
|
||||||
<div id="instancebox">
|
<div id="instancebox"></div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="pagebox">
|
<div class="pagebox">
|
||||||
<h2 id="box3title">Contribute to Jank Client</h2>
|
<h2 id="box3title">Contribute to Jank Client</h2>
|
||||||
<p id="box3description">We always appreciate some help, whether that be in the form of bug reports, code, help translate, or even just pointing out some typos.</p><br>
|
<p id="box3description">
|
||||||
<a href="https://github.com/MathMan05/JankClient" class="TitleButtons">
|
We always appreciate some help, whether that be in the form of bug reports, code, help
|
||||||
Github
|
translate, or even just pointing out some typos.
|
||||||
</a>
|
</p>
|
||||||
|
<br />
|
||||||
|
<a href="https://github.com/MathMan05/JankClient" class="TitleButtons"> Github </a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
<script src="/home.js" type="module"></script>
|
<script src="/home.js" type="module"></script>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,40 +1,56 @@
|
||||||
import { I18n } from "./i18n.js";
|
import {I18n} from "./i18n.js";
|
||||||
import{ mobile }from"./utils/utils.js";
|
import {mobile} from "./utils/utils.js";
|
||||||
console.log(mobile);
|
console.log(mobile);
|
||||||
const serverbox = document.getElementById("instancebox") as HTMLDivElement;
|
const serverbox = document.getElementById("instancebox") as HTMLDivElement;
|
||||||
|
|
||||||
(async ()=>{
|
(async () => {
|
||||||
await I18n.done;
|
await I18n.done;
|
||||||
const openClient=document.getElementById("openClient")
|
const openClient = document.getElementById("openClient");
|
||||||
const welcomeJank=document.getElementById("welcomeJank")
|
const welcomeJank = document.getElementById("welcomeJank");
|
||||||
const box1title=document.getElementById("box1title")
|
const box1title = document.getElementById("box1title");
|
||||||
const box1Items=document.getElementById("box1Items")
|
const box1Items = document.getElementById("box1Items");
|
||||||
const compatableInstances=document.getElementById("compatableInstances")
|
const compatableInstances = document.getElementById("compatableInstances");
|
||||||
const box3title=document.getElementById("box3title")
|
const box3title = document.getElementById("box3title");
|
||||||
const box3description=document.getElementById("box3description")
|
const box3description = document.getElementById("box3description");
|
||||||
if(openClient&&welcomeJank&&compatableInstances&&box3title&&box3description&&box1title&&box1Items){
|
if (
|
||||||
openClient.textContent=I18n.getTranslation("htmlPages.openClient");
|
openClient &&
|
||||||
welcomeJank.textContent=I18n.getTranslation("htmlPages.welcomeJank");
|
welcomeJank &&
|
||||||
box1title.textContent=I18n.getTranslation("htmlPages.box1title");
|
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");
|
compatableInstances.textContent = I18n.getTranslation("htmlPages.compatableInstances");
|
||||||
box3title.textContent=I18n.getTranslation("htmlPages.box3title");
|
box3title.textContent = I18n.getTranslation("htmlPages.box3title");
|
||||||
box3description.textContent=I18n.getTranslation("htmlPages.box3description");
|
box3description.textContent = I18n.getTranslation("htmlPages.box3description");
|
||||||
|
|
||||||
const items=I18n.getTranslation("htmlPages.box1Items").split("|");
|
const items = I18n.getTranslation("htmlPages.box1Items").split("|");
|
||||||
let i=0;
|
let i = 0;
|
||||||
//@ts-ignore ts is being dumb here
|
//@ts-ignore ts is being dumb here
|
||||||
for(const item of box1Items.children){
|
for (const item of box1Items.children) {
|
||||||
(item as HTMLElement).textContent=items[i];
|
(item as HTMLElement).textContent = items[i];
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
console.error(openClient,welcomeJank,compatableInstances,box3title,box3description,box1title,box1Items)
|
console.error(
|
||||||
|
openClient,
|
||||||
|
welcomeJank,
|
||||||
|
compatableInstances,
|
||||||
|
box3title,
|
||||||
|
box3description,
|
||||||
|
box1title,
|
||||||
|
box1Items,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})()
|
})();
|
||||||
|
|
||||||
fetch("/instances.json")
|
fetch("/instances.json")
|
||||||
.then(_=>_.json())
|
.then((_) => _.json())
|
||||||
.then(
|
.then(
|
||||||
async (
|
async (
|
||||||
json: {
|
json: {
|
||||||
|
@ -45,7 +61,7 @@ fetch("/instances.json")
|
||||||
url?: string;
|
url?: string;
|
||||||
display?: boolean;
|
display?: boolean;
|
||||||
online?: boolean;
|
online?: boolean;
|
||||||
uptime: { alltime: number; daytime: number; weektime: number };
|
uptime: {alltime: number; daytime: number; weektime: number};
|
||||||
urls: {
|
urls: {
|
||||||
wellknown: string;
|
wellknown: string;
|
||||||
api: string;
|
api: string;
|
||||||
|
@ -53,68 +69,69 @@ fetch("/instances.json")
|
||||||
gateway: string;
|
gateway: string;
|
||||||
login?: string;
|
login?: string;
|
||||||
};
|
};
|
||||||
}[]
|
}[],
|
||||||
)=>{
|
) => {
|
||||||
await I18n.done;
|
await I18n.done;
|
||||||
console.warn(json);
|
console.warn(json);
|
||||||
for(const instance of json){
|
for (const instance of json) {
|
||||||
if(instance.display === false){
|
if (instance.display === false) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
div.classList.add("flexltr", "instance");
|
div.classList.add("flexltr", "instance");
|
||||||
if(instance.image){
|
if (instance.image) {
|
||||||
const img = document.createElement("img");
|
const img = document.createElement("img");
|
||||||
img.src = instance.image;
|
img.src = instance.image;
|
||||||
div.append(img);
|
div.append(img);
|
||||||
}
|
}
|
||||||
const statbox = document.createElement("div");
|
const statbox = document.createElement("div");
|
||||||
statbox.classList.add("flexttb","flexgrow");
|
statbox.classList.add("flexttb", "flexgrow");
|
||||||
|
|
||||||
{
|
{
|
||||||
const textbox = document.createElement("div");
|
const textbox = document.createElement("div");
|
||||||
textbox.classList.add("flexttb", "instancetextbox");
|
textbox.classList.add("flexttb", "instancetextbox");
|
||||||
const title = document.createElement("h2");
|
const title = document.createElement("h2");
|
||||||
title.innerText = instance.name;
|
title.innerText = instance.name;
|
||||||
if(instance.online !== undefined){
|
if (instance.online !== undefined) {
|
||||||
const status = document.createElement("span");
|
const status = document.createElement("span");
|
||||||
status.innerText = instance.online ? "Online" : "Offline";
|
status.innerText = instance.online ? "Online" : "Offline";
|
||||||
status.classList.add("instanceStatus");
|
status.classList.add("instanceStatus");
|
||||||
title.append(status);
|
title.append(status);
|
||||||
}
|
}
|
||||||
textbox.append(title);
|
textbox.append(title);
|
||||||
if(instance.description || instance.descriptionLong){
|
if (instance.description || instance.descriptionLong) {
|
||||||
const p = document.createElement("p");
|
const p = document.createElement("p");
|
||||||
if(instance.descriptionLong){
|
if (instance.descriptionLong) {
|
||||||
p.innerText = instance.descriptionLong;
|
p.innerText = instance.descriptionLong;
|
||||||
}else if(instance.description){
|
} else if (instance.description) {
|
||||||
p.innerText = instance.description;
|
p.innerText = instance.description;
|
||||||
}
|
}
|
||||||
textbox.append(p);
|
textbox.append(p);
|
||||||
}
|
}
|
||||||
statbox.append(textbox);
|
statbox.append(textbox);
|
||||||
}
|
}
|
||||||
if(instance.uptime){
|
if (instance.uptime) {
|
||||||
const stats = document.createElement("div");
|
const stats = document.createElement("div");
|
||||||
stats.classList.add("flexltr");
|
stats.classList.add("flexltr");
|
||||||
const span = document.createElement("span");
|
const span = document.createElement("span");
|
||||||
span.innerText = I18n.getTranslation("home.uptimeStats",Math.round(
|
span.innerText = I18n.getTranslation(
|
||||||
instance.uptime.alltime * 100
|
"home.uptimeStats",
|
||||||
)+"",Math.round(
|
Math.round(instance.uptime.alltime * 100) + "",
|
||||||
instance.uptime.weektime * 100
|
Math.round(instance.uptime.weektime * 100) + "",
|
||||||
)+"",Math.round(instance.uptime.daytime * 100)+"")
|
Math.round(instance.uptime.daytime * 100) + "",
|
||||||
|
);
|
||||||
stats.append(span);
|
stats.append(span);
|
||||||
statbox.append(stats);
|
statbox.append(stats);
|
||||||
}
|
}
|
||||||
div.append(statbox);
|
div.append(statbox);
|
||||||
div.onclick = _=>{
|
div.onclick = (_) => {
|
||||||
if(instance.online){
|
if (instance.online) {
|
||||||
window.location.href = "/register.html?instance=" + encodeURI(instance.name);
|
window.location.href = "/register.html?instance=" + encodeURI(instance.name);
|
||||||
}else{
|
} else {
|
||||||
alert(I18n.getTranslation("home.warnOffiline"));
|
alert(I18n.getTranslation("home.warnOffiline"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
serverbox.append(div);
|
serverbox.append(div);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
import { Contextmenu } from "./contextmenu.js";
|
import {Contextmenu} from "./contextmenu.js";
|
||||||
import { MarkDown } from "./markdown.js";
|
import {MarkDown} from "./markdown.js";
|
||||||
|
|
||||||
class Hover{
|
class Hover {
|
||||||
str:string|MarkDown|(()=>Promise<MarkDown|string>|MarkDown|string);
|
str: string | MarkDown | (() => Promise<MarkDown | string> | MarkDown | string);
|
||||||
constructor(txt:string|MarkDown|(()=>Promise<MarkDown|string>|MarkDown|string)){
|
constructor(txt: string | MarkDown | (() => Promise<MarkDown | string> | MarkDown | string)) {
|
||||||
this.str=txt;
|
this.str = txt;
|
||||||
}
|
}
|
||||||
addEvent(elm:HTMLElement){
|
addEvent(elm: HTMLElement) {
|
||||||
let timeOut=setTimeout(()=>{},0);
|
let timeOut = setTimeout(() => {}, 0);
|
||||||
let elm2=document.createElement("div");
|
let elm2 = document.createElement("div");
|
||||||
elm.addEventListener("mouseover",()=>{
|
elm.addEventListener("mouseover", () => {
|
||||||
timeOut=setTimeout(async ()=>{
|
timeOut = setTimeout(async () => {
|
||||||
elm2=await this.makeHover(elm);
|
elm2 = await this.makeHover(elm);
|
||||||
},750)
|
}, 750);
|
||||||
});
|
});
|
||||||
elm.addEventListener("mouseout",()=>{
|
elm.addEventListener("mouseout", () => {
|
||||||
clearTimeout(timeOut);
|
clearTimeout(timeOut);
|
||||||
elm2.remove();
|
elm2.remove();
|
||||||
});
|
});
|
||||||
|
@ -22,36 +22,37 @@ class Hover{
|
||||||
if (e[0].removedNodes) {
|
if (e[0].removedNodes) {
|
||||||
clearTimeout(timeOut);
|
clearTimeout(timeOut);
|
||||||
elm2.remove();
|
elm2.remove();
|
||||||
};
|
|
||||||
}).observe(elm,{ childList: true });
|
|
||||||
}
|
}
|
||||||
async makeHover(elm:HTMLElement){
|
}).observe(elm, {childList: true});
|
||||||
if(!document.contains(elm)) return document.createDocumentFragment() as unknown as HTMLDivElement;
|
}
|
||||||
const div=document.createElement("div");
|
async makeHover(elm: HTMLElement) {
|
||||||
if(this.str instanceof MarkDown){
|
if (!document.contains(elm))
|
||||||
div.append(this.str.makeHTML())
|
return document.createDocumentFragment() as unknown as HTMLDivElement;
|
||||||
}else if(this.str instanceof Function){
|
const div = document.createElement("div");
|
||||||
const hover=await this.str();
|
if (this.str instanceof MarkDown) {
|
||||||
if(hover 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());
|
div.append(hover.makeHTML());
|
||||||
}else{
|
} else {
|
||||||
div.innerText=hover;
|
div.innerText = hover;
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
div.innerText=this.str;
|
div.innerText = this.str;
|
||||||
}
|
}
|
||||||
const box=elm.getBoundingClientRect();
|
const box = elm.getBoundingClientRect();
|
||||||
div.style.top=(box.bottom+4)+"px";
|
div.style.top = box.bottom + 4 + "px";
|
||||||
div.style.left=Math.floor(box.left+box.width/2)+"px";
|
div.style.left = Math.floor(box.left + box.width / 2) + "px";
|
||||||
div.classList.add("hoverthing");
|
div.classList.add("hoverthing");
|
||||||
div.style.opacity="0";
|
div.style.opacity = "0";
|
||||||
setTimeout(()=>{
|
setTimeout(() => {
|
||||||
div.style.opacity="1";
|
div.style.opacity = "1";
|
||||||
},10)
|
}, 10);
|
||||||
document.body.append(div);
|
document.body.append(div);
|
||||||
Contextmenu.keepOnScreen(div);
|
Contextmenu.keepOnScreen(div);
|
||||||
console.log(div,elm);
|
console.log(div, elm);
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export{Hover}
|
export {Hover};
|
||||||
|
|
|
@ -1,122 +1,118 @@
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
import {langs} from "./translations/langs.js";
|
import {langs} from "./translations/langs.js";
|
||||||
const langmap=new Map<string,string>();
|
const langmap = new Map<string, string>();
|
||||||
for(const lang of Object.keys(langs) as string[]){
|
for (const lang of Object.keys(langs) as string[]) {
|
||||||
langmap.set(lang,langs[lang]);
|
langmap.set(lang, langs[lang]);
|
||||||
}
|
}
|
||||||
console.log(langs);
|
console.log(langs);
|
||||||
type translation={
|
type translation = {
|
||||||
[key:string]:string|translation
|
[key: string]: string | translation;
|
||||||
};
|
};
|
||||||
let res:()=>unknown=()=>{};
|
let res: () => unknown = () => {};
|
||||||
class I18n{
|
class I18n {
|
||||||
static lang:string;
|
static lang: string;
|
||||||
static translations:translation[]=[];
|
static translations: translation[] = [];
|
||||||
static done=new Promise<void>((res2,_reject)=>{
|
static done = new Promise<void>((res2, _reject) => {
|
||||||
res=res2;
|
res = res2;
|
||||||
});
|
});
|
||||||
static async create(lang:string){
|
static async create(lang: string) {
|
||||||
|
const json = (await (await fetch("/translations/" + lang + ".json")).json()) as translation;
|
||||||
const json=await (await fetch("/translations/"+lang+".json")).json() as translation;
|
const translations: translation[] = [];
|
||||||
const translations:translation[]=[];
|
|
||||||
translations.push(json);
|
translations.push(json);
|
||||||
if(lang!=="en"){
|
if (lang !== "en") {
|
||||||
translations.push(await (await fetch("/translations/en.json")).json() as translation);
|
translations.push((await (await fetch("/translations/en.json")).json()) as translation);
|
||||||
}
|
}
|
||||||
this.lang=lang;
|
this.lang = lang;
|
||||||
this.translations=translations;
|
this.translations = translations;
|
||||||
|
|
||||||
res();
|
res();
|
||||||
}
|
}
|
||||||
static getTranslation(msg:string,...params:string[]):string{
|
static getTranslation(msg: string, ...params: string[]): string {
|
||||||
let str:string|undefined;
|
let str: string | undefined;
|
||||||
const path=msg.split(".");
|
const path = msg.split(".");
|
||||||
for(const json of this.translations){
|
for (const json of this.translations) {
|
||||||
let jsont:string|translation=json;
|
let jsont: string | translation = json;
|
||||||
for(const thing of path){
|
for (const thing of path) {
|
||||||
if(typeof jsont !== "string" && jsont!==undefined){
|
if (typeof jsont !== "string" && jsont !== undefined) {
|
||||||
jsont=jsont[thing];
|
jsont = jsont[thing];
|
||||||
|
} else {
|
||||||
}else{
|
jsont = json;
|
||||||
jsont=json;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(typeof jsont === "string"){
|
if (typeof jsont === "string") {
|
||||||
str=jsont;
|
str = jsont;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(str){
|
if (str) {
|
||||||
return this.fillInBlanks(str,params);
|
return this.fillInBlanks(str, params);
|
||||||
}else{
|
} else {
|
||||||
throw new Error(msg+" not found")
|
throw new Error(msg + " not found");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static fillInBlanks(msg:string,params:string[]):string{
|
static fillInBlanks(msg: string, params: string[]): string {
|
||||||
//thanks to geotale for the regex
|
//thanks to geotale for the regex
|
||||||
msg=msg.replace(/\$\d+/g,(match) => {
|
msg = msg.replace(/\$\d+/g, (match) => {
|
||||||
const number=Number(match.slice(1));
|
const number = Number(match.slice(1));
|
||||||
if(params[number-1]){
|
if (params[number - 1]) {
|
||||||
return params[number-1];
|
return params[number - 1];
|
||||||
}else{
|
} else {
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
msg=msg.replace(/{{(.+?)}}/g,
|
msg = msg.replace(/{{(.+?)}}/g, (str, match: string) => {
|
||||||
(str, match:string) => {
|
const [op, strsSplit] = this.fillInBlanks(match, params).split(":");
|
||||||
const [op,strsSplit]=this.fillInBlanks(match,params).split(":");
|
const [first, ...strs] = strsSplit.split("|");
|
||||||
const [first,...strs]=strsSplit.split("|");
|
switch (op.toUpperCase()) {
|
||||||
switch(op.toUpperCase()){
|
case "PLURAL": {
|
||||||
case "PLURAL":{
|
const numb = Number(first);
|
||||||
const numb=Number(first);
|
if (numb === 0) {
|
||||||
if(numb===0){
|
return strs[strs.length - 1];
|
||||||
return strs[strs.length-1];
|
|
||||||
}
|
}
|
||||||
return strs[Math.min(strs.length-1,numb-1)];
|
return strs[Math.min(strs.length - 1, numb - 1)];
|
||||||
}
|
}
|
||||||
case "GENDER":{
|
case "GENDER": {
|
||||||
if(first==="male"){
|
if (first === "male") {
|
||||||
return strs[0];
|
return strs[0];
|
||||||
}else if(first==="female"){
|
} else if (first === "female") {
|
||||||
return strs[1];
|
return strs[1];
|
||||||
}else if(first==="neutral"){
|
} else if (first === "neutral") {
|
||||||
if(strs[2]){
|
if (strs[2]) {
|
||||||
return strs[2];
|
return strs[2];
|
||||||
}else{
|
} else {
|
||||||
return strs[0];
|
return strs[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
static options(){
|
static options() {
|
||||||
return [...langmap.keys()].map(e=>e.replace(".json",""));
|
return [...langmap.keys()].map((e) => e.replace(".json", ""));
|
||||||
}
|
}
|
||||||
static setLanguage(lang:string){
|
static setLanguage(lang: string) {
|
||||||
if(this.options().indexOf(userLocale)!==-1){
|
if (this.options().indexOf(userLocale) !== -1) {
|
||||||
localStorage.setItem("lang",lang);
|
localStorage.setItem("lang", lang);
|
||||||
I18n.create(lang);
|
I18n.create(lang);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(langmap);
|
console.log(langmap);
|
||||||
let userLocale = navigator.language.slice(0,2) || "en";
|
let userLocale = navigator.language.slice(0, 2) || "en";
|
||||||
if(I18n.options().indexOf(userLocale)===-1){
|
if (I18n.options().indexOf(userLocale) === -1) {
|
||||||
userLocale="en";
|
userLocale = "en";
|
||||||
}
|
}
|
||||||
const storage=localStorage.getItem("lang");
|
const storage = localStorage.getItem("lang");
|
||||||
if(storage){
|
if (storage) {
|
||||||
userLocale=storage;
|
userLocale = storage;
|
||||||
}else{
|
} else {
|
||||||
localStorage.setItem("lang",userLocale)
|
localStorage.setItem("lang", userLocale);
|
||||||
}
|
}
|
||||||
I18n.create(userLocale);
|
I18n.create(userLocale);
|
||||||
|
|
||||||
export{I18n,langmap};
|
export {I18n, langmap};
|
||||||
|
|
|
@ -1,23 +1,37 @@
|
||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
||||||
|
/>
|
||||||
<title>Jank Client</title>
|
<title>Jank Client</title>
|
||||||
<meta content="Jank Client" property="og:title">
|
<meta content="Jank Client" property="og:title" />
|
||||||
<meta content="A spacebar client that has DMs, replying and more" property="og:description">
|
<meta content="A spacebar client that has DMs, replying and more" property="og:description" />
|
||||||
<meta content="/logo.webp" property="og:image">
|
<meta content="/logo.webp" property="og:image" />
|
||||||
<meta content="#4b458c" data-react-helmet="true" name="theme-color">
|
<meta content="#4b458c" data-react-helmet="true" name="theme-color" />
|
||||||
<link href="/style.css" rel="stylesheet">
|
<link href="/style.css" rel="stylesheet" />
|
||||||
<link href="/themes.css" rel="stylesheet" id="lightcss">
|
<link href="/themes.css" rel="stylesheet" id="lightcss" />
|
||||||
<style>body.no-theme,#loading{background:#16191b;}@media(prefers-color-scheme:light){body.no-theme,#loading{background:#9397bd;}}</style>
|
<style>
|
||||||
<link rel="manifest" href="/manifest.json">
|
body.no-theme,
|
||||||
|
#loading {
|
||||||
|
background: #16191b;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
body.no-theme,
|
||||||
|
#loading {
|
||||||
|
background: #9397bd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<link rel="manifest" href="/manifest.json" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="no-theme">
|
<body class="no-theme">
|
||||||
<div id="loading" class="loading">
|
<div id="loading" class="loading">
|
||||||
<div class="centeritem">
|
<div class="centeritem">
|
||||||
<img src="/logo.svg" style="width:3in;height:3in;">
|
<img src="/logo.svg" style="width: 3in; height: 3in" />
|
||||||
<h1 id="loadingText">Jank Client is loading</h1>
|
<h1 id="loadingText">Jank Client is loading</h1>
|
||||||
<h2 id="load-desc">This shouldn't take long</h2>
|
<h2 id="load-desc">This shouldn't take long</h2>
|
||||||
<h1 id="switchaccounts">Switch Accounts</h1>
|
<h1 id="switchaccounts">Switch Accounts</h1>
|
||||||
|
@ -36,7 +50,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="flexltr" id="userdock">
|
<div class="flexltr" id="userdock">
|
||||||
<div class="flexltr" id="userinfo">
|
<div class="flexltr" id="userinfo">
|
||||||
<img id="userpfp" class="pfp">
|
<img id="userpfp" class="pfp" />
|
||||||
|
|
||||||
<div class="flexttb userflex">
|
<div class="flexttb userflex">
|
||||||
<p id="username">USERNAME</p>
|
<p id="username">USERNAME</p>
|
||||||
|
@ -55,8 +69,8 @@
|
||||||
<label for="maintoggle" id="maintoggleicon">
|
<label for="maintoggle" id="maintoggleicon">
|
||||||
<span class="svgicon svg-category"></span>
|
<span class="svgicon svg-category"></span>
|
||||||
</label>
|
</label>
|
||||||
<input type="checkbox" id="maintoggle">
|
<input type="checkbox" id="maintoggle" />
|
||||||
<span class="flexltr" style="align-items: center;">
|
<span class="flexltr" style="align-items: center">
|
||||||
<span id="channelname">Channel name</span>
|
<span id="channelname">Channel name</span>
|
||||||
<span id="channelTopic" class="ellipsis" hidden>Channel topic</span>
|
<span id="channelTopic" class="ellipsis" hidden>Channel topic</span>
|
||||||
</span>
|
</span>
|
||||||
|
@ -64,15 +78,14 @@
|
||||||
<label for="memberlisttoggle" id="memberlisttoggleicon">
|
<label for="memberlisttoggle" id="memberlisttoggleicon">
|
||||||
<span class="svgicon svg-friends"></span>
|
<span class="svgicon svg-friends"></span>
|
||||||
</label>
|
</label>
|
||||||
<input type="checkbox" id="memberlisttoggle" checked>
|
<input type="checkbox" id="memberlisttoggle" checked />
|
||||||
</div>
|
</div>
|
||||||
<div class="flexltr flexgrow">
|
<div class="flexltr flexgrow">
|
||||||
<div class="flexttb flexgrow">
|
<div class="flexttb flexgrow">
|
||||||
<div id="channelw" class="flexltr">
|
<div id="channelw" class="flexltr">
|
||||||
<div id="loadingdiv">
|
<div id="loadingdiv"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div style="position: relative">
|
||||||
<div style="position: relative;">
|
|
||||||
<div id="searchOptions" class="flexttb searchOptions"></div>
|
<div id="searchOptions" class="flexttb searchOptions"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="pasteimage" class="flexltr"></div>
|
<div id="pasteimage" class="flexltr"></div>
|
||||||
|
@ -81,7 +94,7 @@
|
||||||
<div id="realbox">
|
<div id="realbox">
|
||||||
<div class="outerTypeBox">
|
<div class="outerTypeBox">
|
||||||
<span class="svg-upload svgicon" id="upload"></span>
|
<span class="svg-upload svgicon" id="upload"></span>
|
||||||
<div id="typebox" contentEditable="true"></div>
|
<div id="typebox" contenteditable="true"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="typing" class="hidden flexltr">
|
<div id="typing" class="hidden flexltr">
|
||||||
|
|
|
@ -1,36 +1,36 @@
|
||||||
import{ Localuser }from"./localuser.js";
|
import {Localuser} from "./localuser.js";
|
||||||
import{ Contextmenu }from"./contextmenu.js";
|
import {Contextmenu} from "./contextmenu.js";
|
||||||
import{ mobile }from"./utils/utils.js";
|
import {mobile} from "./utils/utils.js";
|
||||||
import { getBulkUsers, setTheme, Specialuser } from "./utils/utils.js";
|
import {getBulkUsers, setTheme, Specialuser} from "./utils/utils.js";
|
||||||
import{ MarkDown }from"./markdown.js";
|
import {MarkDown} from "./markdown.js";
|
||||||
import{ Message }from"./message.js";
|
import {Message} from "./message.js";
|
||||||
import{File}from"./file.js";
|
import {File} from "./file.js";
|
||||||
import { I18n } from "./i18n.js";
|
import {I18n} from "./i18n.js";
|
||||||
(async ()=>{
|
(async () => {
|
||||||
await I18n.done
|
await I18n.done;
|
||||||
const users = getBulkUsers();
|
const users = getBulkUsers();
|
||||||
if(!users.currentuser){
|
if (!users.currentuser) {
|
||||||
window.location.href = "/login.html";
|
window.location.href = "/login.html";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const loadingText=document.getElementById("loadingText");
|
const loadingText = document.getElementById("loadingText");
|
||||||
const loaddesc=document.getElementById("load-desc");
|
const loaddesc = document.getElementById("load-desc");
|
||||||
const switchaccounts=document.getElementById("switchaccounts");
|
const switchaccounts = document.getElementById("switchaccounts");
|
||||||
const filedroptext=document.getElementById("filedroptext");
|
const filedroptext = document.getElementById("filedroptext");
|
||||||
if(loadingText&&loaddesc&&switchaccounts&&filedroptext){
|
if (loadingText && loaddesc && switchaccounts && filedroptext) {
|
||||||
loadingText.textContent=I18n.getTranslation("htmlPages.loadingText");
|
loadingText.textContent = I18n.getTranslation("htmlPages.loadingText");
|
||||||
loaddesc.textContent=I18n.getTranslation("htmlPages.loaddesc");
|
loaddesc.textContent = I18n.getTranslation("htmlPages.loaddesc");
|
||||||
switchaccounts.textContent=I18n.getTranslation("htmlPages.switchaccounts");
|
switchaccounts.textContent = I18n.getTranslation("htmlPages.switchaccounts");
|
||||||
filedroptext.textContent=I18n.getTranslation("uploadFilesText");
|
filedroptext.textContent = I18n.getTranslation("uploadFilesText");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
I18n
|
I18n;
|
||||||
function showAccountSwitcher(): void{
|
function showAccountSwitcher(): void {
|
||||||
const table = document.createElement("div");
|
const table = document.createElement("div");
|
||||||
table.classList.add("flexttb","accountSwitcher");
|
table.classList.add("flexttb", "accountSwitcher");
|
||||||
|
|
||||||
for(const user of Object.values(users.users)){
|
for (const user of Object.values(users.users)) {
|
||||||
const specialUser = user as Specialuser;
|
const specialUser = user as Specialuser;
|
||||||
const userInfo = document.createElement("div");
|
const userInfo = document.createElement("div");
|
||||||
userInfo.classList.add("flexltr", "switchtable");
|
userInfo.classList.add("flexltr", "switchtable");
|
||||||
|
@ -55,7 +55,7 @@ import { I18n } from "./i18n.js";
|
||||||
userInfo.append(userDiv);
|
userInfo.append(userDiv);
|
||||||
table.append(userInfo);
|
table.append(userInfo);
|
||||||
|
|
||||||
userInfo.addEventListener("click", ()=>{
|
userInfo.addEventListener("click", () => {
|
||||||
thisUser.unload();
|
thisUser.unload();
|
||||||
thisUser.swapped = true;
|
thisUser.swapped = true;
|
||||||
const loading = document.getElementById("loading") as HTMLDivElement;
|
const loading = document.getElementById("loading") as HTMLDivElement;
|
||||||
|
@ -66,7 +66,7 @@ import { I18n } from "./i18n.js";
|
||||||
users.currentuser = specialUser.uid;
|
users.currentuser = specialUser.uid;
|
||||||
localStorage.setItem("userinfos", JSON.stringify(users));
|
localStorage.setItem("userinfos", JSON.stringify(users));
|
||||||
|
|
||||||
thisUser.initwebsocket().then(()=>{
|
thisUser.initwebsocket().then(() => {
|
||||||
thisUser.loaduser();
|
thisUser.loaduser();
|
||||||
thisUser.init();
|
thisUser.init();
|
||||||
loading.classList.add("doneloading");
|
loading.classList.add("doneloading");
|
||||||
|
@ -81,12 +81,12 @@ import { I18n } from "./i18n.js";
|
||||||
const switchAccountDiv = document.createElement("div");
|
const switchAccountDiv = document.createElement("div");
|
||||||
switchAccountDiv.classList.add("switchtable");
|
switchAccountDiv.classList.add("switchtable");
|
||||||
switchAccountDiv.textContent = I18n.getTranslation("switchAccounts");
|
switchAccountDiv.textContent = I18n.getTranslation("switchAccounts");
|
||||||
switchAccountDiv.addEventListener("click", ()=>{
|
switchAccountDiv.addEventListener("click", () => {
|
||||||
window.location.href = "/login.html";
|
window.location.href = "/login.html";
|
||||||
});
|
});
|
||||||
table.append(switchAccountDiv);
|
table.append(switchAccountDiv);
|
||||||
|
|
||||||
if(Contextmenu.currentmenu){
|
if (Contextmenu.currentmenu) {
|
||||||
Contextmenu.currentmenu.remove();
|
Contextmenu.currentmenu.remove();
|
||||||
}
|
}
|
||||||
Contextmenu.currentmenu = table;
|
Contextmenu.currentmenu = table;
|
||||||
|
@ -94,22 +94,22 @@ import { I18n } from "./i18n.js";
|
||||||
}
|
}
|
||||||
|
|
||||||
const userInfoElement = document.getElementById("userinfo") as HTMLDivElement;
|
const userInfoElement = document.getElementById("userinfo") as HTMLDivElement;
|
||||||
userInfoElement.addEventListener("click", event=>{
|
userInfoElement.addEventListener("click", (event) => {
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
showAccountSwitcher();
|
showAccountSwitcher();
|
||||||
});
|
});
|
||||||
|
|
||||||
const switchAccountsElement = document.getElementById("switchaccounts") as HTMLDivElement;
|
const switchAccountsElement = document.getElementById("switchaccounts") as HTMLDivElement;
|
||||||
switchAccountsElement.addEventListener("click", event=>{
|
switchAccountsElement.addEventListener("click", (event) => {
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
showAccountSwitcher();
|
showAccountSwitcher();
|
||||||
});
|
});
|
||||||
|
|
||||||
let thisUser: Localuser;
|
let thisUser: Localuser;
|
||||||
try{
|
try {
|
||||||
console.log(users.users, users.currentuser);
|
console.log(users.users, users.currentuser);
|
||||||
thisUser = new Localuser(users.users[users.currentuser]);
|
thisUser = new Localuser(users.users[users.currentuser]);
|
||||||
thisUser.initwebsocket().then(()=>{
|
thisUser.initwebsocket().then(() => {
|
||||||
thisUser.loaduser();
|
thisUser.loaduser();
|
||||||
thisUser.init();
|
thisUser.init();
|
||||||
const loading = document.getElementById("loading") as HTMLDivElement;
|
const loading = document.getElementById("loading") as HTMLDivElement;
|
||||||
|
@ -117,62 +117,65 @@ import { I18n } from "./i18n.js";
|
||||||
loading.classList.remove("loading");
|
loading.classList.remove("loading");
|
||||||
console.log("done loading");
|
console.log("done loading");
|
||||||
});
|
});
|
||||||
}catch(e){
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
(document.getElementById("load-desc") as HTMLSpanElement).textContent = I18n.getTranslation("accountNotStart");
|
(document.getElementById("load-desc") as HTMLSpanElement).textContent =
|
||||||
|
I18n.getTranslation("accountNotStart");
|
||||||
thisUser = new Localuser(-1);
|
thisUser = new Localuser(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const menu = new Contextmenu<void,void>("create rightclick");
|
const menu = new Contextmenu<void, void>("create rightclick");
|
||||||
menu.addbutton(
|
menu.addbutton(
|
||||||
I18n.getTranslation("channel.createChannel"),
|
I18n.getTranslation("channel.createChannel"),
|
||||||
()=>{
|
() => {
|
||||||
if(thisUser.lookingguild){
|
if (thisUser.lookingguild) {
|
||||||
thisUser.lookingguild.createchannels();
|
thisUser.lookingguild.createchannels();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
()=>thisUser.isAdmin()
|
() => thisUser.isAdmin(),
|
||||||
);
|
);
|
||||||
|
|
||||||
menu.addbutton(
|
menu.addbutton(
|
||||||
I18n.getTranslation("channel.createCatagory"),
|
I18n.getTranslation("channel.createCatagory"),
|
||||||
()=>{
|
() => {
|
||||||
if(thisUser.lookingguild){
|
if (thisUser.lookingguild) {
|
||||||
thisUser.lookingguild.createcategory();
|
thisUser.lookingguild.createcategory();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
()=>thisUser.isAdmin()
|
() => thisUser.isAdmin(),
|
||||||
);
|
);
|
||||||
|
|
||||||
menu.bindContextmenu(document.getElementById("channels") as HTMLDivElement);
|
menu.bindContextmenu(document.getElementById("channels") as HTMLDivElement);
|
||||||
|
|
||||||
const pasteImageElement = document.getElementById("pasteimage") as HTMLDivElement;
|
const pasteImageElement = document.getElementById("pasteimage") as HTMLDivElement;
|
||||||
let replyingTo: Message | null = null;
|
let replyingTo: Message | null = null;
|
||||||
window.addEventListener("popstate",(e)=>{
|
window.addEventListener("popstate", (e) => {
|
||||||
if(e.state instanceof Object){
|
if (e.state instanceof Object) {
|
||||||
thisUser.goToChannel(e.state[1],false);
|
thisUser.goToChannel(e.state[1], false);
|
||||||
}
|
}
|
||||||
//console.log(e.state,"state:3")
|
//console.log(e.state,"state:3")
|
||||||
})
|
});
|
||||||
async function handleEnter(event: KeyboardEvent): Promise<void>{
|
async function handleEnter(event: KeyboardEvent): Promise<void> {
|
||||||
if(thisUser.keyup(event)){return}
|
if (thisUser.keyup(event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const channel = thisUser.channelfocus;
|
const channel = thisUser.channelfocus;
|
||||||
if(!channel)return;
|
if (!channel) return;
|
||||||
if(markdown.rawString===""&&event.key==="ArrowUp"){
|
if (markdown.rawString === "" && event.key === "ArrowUp") {
|
||||||
channel.editLast();
|
channel.editLast();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
channel.typingstart();
|
channel.typingstart();
|
||||||
|
|
||||||
if(event.key === "Enter" && !event.shiftKey){
|
if (event.key === "Enter" && !event.shiftKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
replyingTo = thisUser.channelfocus? thisUser.channelfocus.replyingto: null;
|
replyingTo = thisUser.channelfocus ? thisUser.channelfocus.replyingto : null;
|
||||||
if(replyingTo?.div){
|
if (replyingTo?.div) {
|
||||||
replyingTo.div.classList.remove("replying");
|
replyingTo.div.classList.remove("replying");
|
||||||
}
|
}
|
||||||
if(thisUser.channelfocus){
|
if (thisUser.channelfocus) {
|
||||||
thisUser.channelfocus.replyingto = null;
|
thisUser.channelfocus.replyingto = null;
|
||||||
}
|
}
|
||||||
channel.sendMessage(markdown.rawString, {
|
channel.sendMessage(markdown.rawString, {
|
||||||
|
@ -181,10 +184,10 @@ import { I18n } from "./i18n.js";
|
||||||
embeds: [], // Add an empty array for the embeds property
|
embeds: [], // Add an empty array for the embeds property
|
||||||
replyingto: replyingTo,
|
replyingto: replyingTo,
|
||||||
});
|
});
|
||||||
if(thisUser.channelfocus){
|
if (thisUser.channelfocus) {
|
||||||
thisUser.channelfocus.makereplybox();
|
thisUser.channelfocus.makereplybox();
|
||||||
}
|
}
|
||||||
while(images.length){
|
while (images.length) {
|
||||||
images.pop();
|
images.pop();
|
||||||
pasteImageElement.removeChild(imagesHtml.pop() as HTMLElement);
|
pasteImageElement.removeChild(imagesHtml.pop() as HTMLElement);
|
||||||
}
|
}
|
||||||
|
@ -193,15 +196,17 @@ import { I18n } from "./i18n.js";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CustomHTMLDivElement extends HTMLDivElement {markdown: MarkDown;}
|
interface CustomHTMLDivElement extends HTMLDivElement {
|
||||||
|
markdown: MarkDown;
|
||||||
|
}
|
||||||
|
|
||||||
const typebox = document.getElementById("typebox") as CustomHTMLDivElement;
|
const typebox = document.getElementById("typebox") as CustomHTMLDivElement;
|
||||||
const markdown = new MarkDown("", thisUser);
|
const markdown = new MarkDown("", thisUser);
|
||||||
typebox.markdown = markdown;
|
typebox.markdown = markdown;
|
||||||
typebox.addEventListener("keyup", handleEnter);
|
typebox.addEventListener("keyup", handleEnter);
|
||||||
typebox.addEventListener("keydown", event=>{
|
typebox.addEventListener("keydown", (event) => {
|
||||||
thisUser.keydown(event)
|
thisUser.keydown(event);
|
||||||
if(event.key === "Enter" && !event.shiftKey) event.preventDefault();
|
if (event.key === "Enter" && !event.shiftKey) event.preventDefault();
|
||||||
});
|
});
|
||||||
markdown.giveBox(typebox);
|
markdown.giveBox(typebox);
|
||||||
{
|
{
|
||||||
|
@ -209,30 +214,27 @@ import { I18n } from "./i18n.js";
|
||||||
const markdown = new MarkDown("", thisUser);
|
const markdown = new MarkDown("", thisUser);
|
||||||
searchBox.markdown = markdown;
|
searchBox.markdown = markdown;
|
||||||
|
|
||||||
searchBox.addEventListener("keydown", event=>{
|
searchBox.addEventListener("keydown", (event) => {
|
||||||
|
if (event.key === "Enter") {
|
||||||
if(event.key === "Enter") {
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
thisUser.mSearch(markdown.rawString)
|
thisUser.mSearch(markdown.rawString);
|
||||||
};
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
markdown.giveBox(searchBox);
|
markdown.giveBox(searchBox);
|
||||||
markdown.setCustomBox((e)=>{
|
markdown.setCustomBox((e) => {
|
||||||
const span=document.createElement("span");
|
const span = document.createElement("span");
|
||||||
span.textContent=e.replace("\n","");
|
span.textContent = e.replace("\n", "");
|
||||||
return span;
|
return span;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
const images: Blob[] = [];
|
const images: Blob[] = [];
|
||||||
const imagesHtml: HTMLElement[] = [];
|
const imagesHtml: HTMLElement[] = [];
|
||||||
|
|
||||||
document.addEventListener("paste", async (e: ClipboardEvent)=>{
|
document.addEventListener("paste", async (e: ClipboardEvent) => {
|
||||||
if(!e.clipboardData)return;
|
if (!e.clipboardData) return;
|
||||||
|
|
||||||
for(const file of Array.from(e.clipboardData.files)){
|
for (const file of Array.from(e.clipboardData.files)) {
|
||||||
const fileInstance = File.initFromBlob(file);
|
const fileInstance = File.initFromBlob(file);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const html = fileInstance.upHTML(images, file);
|
const html = fileInstance.upHTML(images, file);
|
||||||
|
@ -244,60 +246,59 @@ import { I18n } from "./i18n.js";
|
||||||
|
|
||||||
setTheme();
|
setTheme();
|
||||||
|
|
||||||
function userSettings(): void{
|
function userSettings(): void {
|
||||||
thisUser.showusersettings();
|
thisUser.showusersettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
(document.getElementById("settings") as HTMLImageElement).onclick =
|
(document.getElementById("settings") as HTMLImageElement).onclick = userSettings;
|
||||||
userSettings;
|
|
||||||
|
|
||||||
if(mobile){
|
if (mobile) {
|
||||||
const channelWrapper = document.getElementById("channelw") as HTMLDivElement;
|
const channelWrapper = document.getElementById("channelw") as HTMLDivElement;
|
||||||
channelWrapper.onclick = ()=>{
|
channelWrapper.onclick = () => {
|
||||||
const toggle = document.getElementById("maintoggle") as HTMLInputElement;
|
const toggle = document.getElementById("maintoggle") as HTMLInputElement;
|
||||||
toggle.checked = true;
|
toggle.checked = true;
|
||||||
};
|
};
|
||||||
const memberListToggle = document.getElementById("memberlisttoggle") as HTMLInputElement;
|
const memberListToggle = document.getElementById("memberlisttoggle") as HTMLInputElement;
|
||||||
memberListToggle.checked = false;
|
memberListToggle.checked = false;
|
||||||
}
|
}
|
||||||
let dragendtimeout=setTimeout(()=>{})
|
let dragendtimeout = setTimeout(() => {});
|
||||||
document.addEventListener("dragover",(e)=>{
|
document.addEventListener("dragover", (e) => {
|
||||||
clearTimeout(dragendtimeout);
|
clearTimeout(dragendtimeout);
|
||||||
const data = e.dataTransfer;
|
const data = e.dataTransfer;
|
||||||
const bg=document.getElementById("gimmefile") as HTMLDivElement;
|
const bg = document.getElementById("gimmefile") as HTMLDivElement;
|
||||||
|
|
||||||
if(data){
|
if (data) {
|
||||||
const isfile=data.types.includes("Files")||data.types.includes("application/x-moz-file");
|
const isfile = data.types.includes("Files") || data.types.includes("application/x-moz-file");
|
||||||
if(!isfile){
|
if (!isfile) {
|
||||||
bg.hidden=true;
|
bg.hidden = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
bg.hidden=false;
|
bg.hidden = false;
|
||||||
//console.log(data.types,data)
|
//console.log(data.types,data)
|
||||||
}else{
|
} else {
|
||||||
bg.hidden=true;
|
bg.hidden = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
document.addEventListener("dragleave",(_)=>{
|
document.addEventListener("dragleave", (_) => {
|
||||||
dragendtimeout=setTimeout(()=>{
|
dragendtimeout = setTimeout(() => {
|
||||||
const bg=document.getElementById("gimmefile") as HTMLDivElement;
|
const bg = document.getElementById("gimmefile") as HTMLDivElement;
|
||||||
bg.hidden=true;
|
bg.hidden = true;
|
||||||
},1000)
|
}, 1000);
|
||||||
});
|
});
|
||||||
document.addEventListener("dragenter",(e)=>{
|
document.addEventListener("dragenter", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
})
|
});
|
||||||
document.addEventListener("drop",e=>{
|
document.addEventListener("drop", (e) => {
|
||||||
const data = e.dataTransfer;
|
const data = e.dataTransfer;
|
||||||
const bg=document.getElementById("gimmefile") as HTMLDivElement;
|
const bg = document.getElementById("gimmefile") as HTMLDivElement;
|
||||||
bg.hidden=true;
|
bg.hidden = true;
|
||||||
if(data){
|
if (data) {
|
||||||
const isfile=data.types.includes("Files")||data.types.includes("application/x-moz-file");
|
const isfile = data.types.includes("Files") || data.types.includes("application/x-moz-file");
|
||||||
if(isfile){
|
if (isfile) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
console.log(data.files);
|
console.log(data.files);
|
||||||
for(const file of Array.from(data.files)){
|
for (const file of Array.from(data.files)) {
|
||||||
const fileInstance = File.initFromBlob(file);
|
const fileInstance = File.initFromBlob(file);
|
||||||
const html = fileInstance.upHTML(images, file);
|
const html = fileInstance.upHTML(images, file);
|
||||||
pasteImageElement.appendChild(html);
|
pasteImageElement.appendChild(html);
|
||||||
|
@ -307,14 +308,14 @@ import { I18n } from "./i18n.js";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
(document.getElementById("upload") as HTMLElement).onclick=()=>{
|
(document.getElementById("upload") as HTMLElement).onclick = () => {
|
||||||
const input=document.createElement("input");
|
const input = document.createElement("input");
|
||||||
input.type="file";
|
input.type = "file";
|
||||||
input.click();
|
input.click();
|
||||||
console.log("clicked")
|
console.log("clicked");
|
||||||
input.onchange=(() => {
|
input.onchange = () => {
|
||||||
if(input.files){
|
if (input.files) {
|
||||||
for(const file of Array.from(input.files)){
|
for (const file of Array.from(input.files)) {
|
||||||
const fileInstance = File.initFromBlob(file);
|
const fileInstance = File.initFromBlob(file);
|
||||||
const html = fileInstance.upHTML(images, file);
|
const html = fileInstance.upHTML(images, file);
|
||||||
pasteImageElement.appendChild(html);
|
pasteImageElement.appendChild(html);
|
||||||
|
@ -322,7 +323,6 @@ import { I18n } from "./i18n.js";
|
||||||
imagesHtml.push(html);
|
imagesHtml.push(html);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
class InfiniteScroller{
|
class InfiniteScroller {
|
||||||
readonly getIDFromOffset: (
|
readonly getIDFromOffset: (ID: string, offset: number) => Promise<string | undefined>;
|
||||||
ID: string,
|
|
||||||
offset: number
|
|
||||||
) => Promise<string | undefined>;
|
|
||||||
readonly getHTMLFromID: (ID: string) => Promise<HTMLElement>;
|
readonly getHTMLFromID: (ID: string) => Promise<HTMLElement>;
|
||||||
readonly destroyFromID: (ID: string) => Promise<boolean>;
|
readonly destroyFromID: (ID: string) => Promise<boolean>;
|
||||||
readonly reachesBottom: () => void;
|
readonly reachesBottom: () => void;
|
||||||
|
@ -19,39 +16,39 @@ offset: number
|
||||||
averageheight = 60;
|
averageheight = 60;
|
||||||
watchtime = false;
|
watchtime = false;
|
||||||
changePromise: Promise<boolean> | undefined;
|
changePromise: Promise<boolean> | undefined;
|
||||||
scollDiv!: { scrollTop: number; scrollHeight: number; clientHeight: number };
|
scollDiv!: {scrollTop: number; scrollHeight: number; clientHeight: number};
|
||||||
|
|
||||||
resetVars(){
|
resetVars() {
|
||||||
this.scrollTop=0;
|
this.scrollTop = 0;
|
||||||
this.scrollBottom=0;
|
this.scrollBottom = 0;
|
||||||
this.averageheight=60;
|
this.averageheight = 60;
|
||||||
this.watchtime=false;
|
this.watchtime = false;
|
||||||
this.needsupdate=true;
|
this.needsupdate = true;
|
||||||
this.beenloaded=false;
|
this.beenloaded = false;
|
||||||
this.changePromise=undefined;
|
this.changePromise = undefined;
|
||||||
if(this.timeout){
|
if (this.timeout) {
|
||||||
clearTimeout(this.timeout);
|
clearTimeout(this.timeout);
|
||||||
this.timeout=null;
|
this.timeout = null;
|
||||||
}
|
}
|
||||||
for(const thing of this.HTMLElements){
|
for (const thing of this.HTMLElements) {
|
||||||
this.destroyFromID(thing[1]);
|
this.destroyFromID(thing[1]);
|
||||||
}
|
}
|
||||||
this.HTMLElements=[];
|
this.HTMLElements = [];
|
||||||
}
|
}
|
||||||
constructor(
|
constructor(
|
||||||
getIDFromOffset: InfiniteScroller["getIDFromOffset"],
|
getIDFromOffset: InfiniteScroller["getIDFromOffset"],
|
||||||
getHTMLFromID: InfiniteScroller["getHTMLFromID"],
|
getHTMLFromID: InfiniteScroller["getHTMLFromID"],
|
||||||
destroyFromID: InfiniteScroller["destroyFromID"],
|
destroyFromID: InfiniteScroller["destroyFromID"],
|
||||||
reachesBottom: InfiniteScroller["reachesBottom"] = ()=>{}
|
reachesBottom: InfiniteScroller["reachesBottom"] = () => {},
|
||||||
){
|
) {
|
||||||
this.getIDFromOffset = getIDFromOffset;
|
this.getIDFromOffset = getIDFromOffset;
|
||||||
this.getHTMLFromID = getHTMLFromID;
|
this.getHTMLFromID = getHTMLFromID;
|
||||||
this.destroyFromID = destroyFromID;
|
this.destroyFromID = destroyFromID;
|
||||||
this.reachesBottom = reachesBottom;
|
this.reachesBottom = reachesBottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDiv(initialId: string): Promise<HTMLDivElement>{
|
async getDiv(initialId: string): Promise<HTMLDivElement> {
|
||||||
if(this.div){
|
if (this.div) {
|
||||||
throw new Error("Div already exists, exiting.");
|
throw new Error("Div already exists, exiting.");
|
||||||
}
|
}
|
||||||
this.resetVars();
|
this.resetVars();
|
||||||
|
@ -59,24 +56,24 @@ offset: number
|
||||||
scroll.classList.add("scroller");
|
scroll.classList.add("scroller");
|
||||||
this.div = scroll;
|
this.div = scroll;
|
||||||
|
|
||||||
this.div.addEventListener("scroll", ()=>{
|
this.div.addEventListener("scroll", () => {
|
||||||
this.checkscroll();
|
this.checkscroll();
|
||||||
if(this.scrollBottom < 5){
|
if (this.scrollBottom < 5) {
|
||||||
this.scrollBottom = 5;
|
this.scrollBottom = 5;
|
||||||
}
|
}
|
||||||
if(this.timeout === null){
|
if (this.timeout === null) {
|
||||||
this.timeout = setTimeout(this.updatestuff.bind(this), 300);
|
this.timeout = setTimeout(this.updatestuff.bind(this), 300);
|
||||||
}
|
}
|
||||||
this.watchForChange();
|
this.watchForChange();
|
||||||
});
|
});
|
||||||
|
|
||||||
let oldheight = 0;
|
let oldheight = 0;
|
||||||
new ResizeObserver(()=>{
|
new ResizeObserver(() => {
|
||||||
this.checkscroll();
|
this.checkscroll();
|
||||||
const func = this.snapBottom();
|
const func = this.snapBottom();
|
||||||
this.updatestuff();
|
this.updatestuff();
|
||||||
const change = oldheight - scroll.offsetHeight;
|
const change = oldheight - scroll.offsetHeight;
|
||||||
if(change > 0 && this.div){
|
if (change > 0 && this.div) {
|
||||||
this.div.scrollTop += change;
|
this.div.scrollTop += change;
|
||||||
}
|
}
|
||||||
oldheight = scroll.offsetHeight;
|
oldheight = scroll.offsetHeight;
|
||||||
|
@ -88,7 +85,7 @@ offset: number
|
||||||
|
|
||||||
await this.firstElement(initialId);
|
await this.firstElement(initialId);
|
||||||
this.updatestuff();
|
this.updatestuff();
|
||||||
await this.watchForChange().then(()=>{
|
await this.watchForChange().then(() => {
|
||||||
this.updatestuff();
|
this.updatestuff();
|
||||||
this.beenloaded = true;
|
this.beenloaded = true;
|
||||||
});
|
});
|
||||||
|
@ -96,76 +93,72 @@ offset: number
|
||||||
return scroll;
|
return scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkscroll(): void{
|
checkscroll(): void {
|
||||||
if(this.beenloaded && this.div && !document.body.contains(this.div)){
|
if (this.beenloaded && this.div && !document.body.contains(this.div)) {
|
||||||
console.warn("not in document");
|
console.warn("not in document");
|
||||||
this.div = null;
|
this.div = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updatestuff(): Promise<void>{
|
async updatestuff(): Promise<void> {
|
||||||
this.timeout = null;
|
this.timeout = null;
|
||||||
if(!this.div)return;
|
if (!this.div) return;
|
||||||
|
|
||||||
this.scrollBottom =
|
this.scrollBottom = this.div.scrollHeight - this.div.scrollTop - this.div.clientHeight;
|
||||||
this.div.scrollHeight - this.div.scrollTop - this.div.clientHeight;
|
|
||||||
this.averageheight = this.div.scrollHeight / this.HTMLElements.length;
|
this.averageheight = this.div.scrollHeight / this.HTMLElements.length;
|
||||||
if(this.averageheight < 10){
|
if (this.averageheight < 10) {
|
||||||
this.averageheight = 60;
|
this.averageheight = 60;
|
||||||
}
|
}
|
||||||
this.scrollTop = this.div.scrollTop;
|
this.scrollTop = this.div.scrollTop;
|
||||||
|
|
||||||
if(!this.scrollBottom && !(await this.watchForChange())){
|
if (!this.scrollBottom && !(await this.watchForChange())) {
|
||||||
this.reachesBottom();
|
this.reachesBottom();
|
||||||
}
|
}
|
||||||
if(!this.scrollTop){
|
if (!this.scrollTop) {
|
||||||
await this.watchForChange();
|
await this.watchForChange();
|
||||||
}
|
}
|
||||||
this.needsupdate = false;
|
this.needsupdate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async firstElement(id: string): Promise<void>{
|
async firstElement(id: string): Promise<void> {
|
||||||
if(!this.div)return;
|
if (!this.div) return;
|
||||||
const html = await this.getHTMLFromID(id);
|
const html = await this.getHTMLFromID(id);
|
||||||
this.div.appendChild(html);
|
this.div.appendChild(html);
|
||||||
this.HTMLElements.push([html, id]);
|
this.HTMLElements.push([html, id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async addedBottom(): Promise<void>{
|
async addedBottom(): Promise<void> {
|
||||||
await this.updatestuff();
|
await this.updatestuff();
|
||||||
const func = this.snapBottom();
|
const func = this.snapBottom();
|
||||||
await this.watchForChange();
|
await this.watchForChange();
|
||||||
func();
|
func();
|
||||||
}
|
}
|
||||||
|
|
||||||
snapBottom(): () => void{
|
snapBottom(): () => void {
|
||||||
const scrollBottom = this.scrollBottom;
|
const scrollBottom = this.scrollBottom;
|
||||||
return()=>{
|
return () => {
|
||||||
if(this.div && scrollBottom < 4){
|
if (this.div && scrollBottom < 4) {
|
||||||
this.div.scrollTop = this.div.scrollHeight;
|
this.div.scrollTop = this.div.scrollHeight;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async watchForTop(
|
private async watchForTop(already = false, fragment = new DocumentFragment()): Promise<boolean> {
|
||||||
already = false,
|
if (!this.div) return false;
|
||||||
fragment = new DocumentFragment()
|
try {
|
||||||
): Promise<boolean>{
|
|
||||||
if(!this.div)return false;
|
|
||||||
try{
|
|
||||||
let again = false;
|
let again = false;
|
||||||
if(this.scrollTop < (already ? this.fillDist : this.minDist)){
|
if (this.scrollTop < (already ? this.fillDist : this.minDist)) {
|
||||||
let nextid: string | undefined;
|
let nextid: string | undefined;
|
||||||
const firstelm = this.HTMLElements.at(0);
|
const firstelm = this.HTMLElements.at(0);
|
||||||
if(firstelm){
|
if (firstelm) {
|
||||||
const previd = firstelm[1];
|
const previd = firstelm[1];
|
||||||
nextid = await this.getIDFromOffset(previd, 1);
|
nextid = await this.getIDFromOffset(previd, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(nextid){
|
if (nextid) {
|
||||||
const html = await this.getHTMLFromID(nextid);
|
const html = await this.getHTMLFromID(nextid);
|
||||||
|
|
||||||
if(!html){
|
if (!html) {
|
||||||
this.destroyFromID(nextid);
|
this.destroyFromID(nextid);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -173,26 +166,24 @@ offset: number
|
||||||
fragment.prepend(html);
|
fragment.prepend(html);
|
||||||
this.HTMLElements.unshift([html, nextid]);
|
this.HTMLElements.unshift([html, nextid]);
|
||||||
this.scrollTop += this.averageheight;
|
this.scrollTop += this.averageheight;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(this.scrollTop > this.maxDist){
|
if (this.scrollTop > this.maxDist) {
|
||||||
const html = this.HTMLElements.shift();
|
const html = this.HTMLElements.shift();
|
||||||
if(html){
|
if (html) {
|
||||||
again = true;
|
again = true;
|
||||||
await this.destroyFromID(html[1]);
|
await this.destroyFromID(html[1]);
|
||||||
|
|
||||||
this.scrollTop -= this.averageheight;
|
this.scrollTop -= this.averageheight;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(again){
|
if (again) {
|
||||||
await this.watchForTop(true, fragment);
|
await this.watchForTop(true, fragment);
|
||||||
}
|
}
|
||||||
return again;
|
return again;
|
||||||
}finally{
|
} finally {
|
||||||
if(!already){
|
if (!already) {
|
||||||
if(this.div.scrollTop === 0){
|
if (this.div.scrollTop === 0) {
|
||||||
this.scrollTop = 1;
|
this.scrollTop = 1;
|
||||||
this.div.scrollTop = 10;
|
this.div.scrollTop = 10;
|
||||||
}
|
}
|
||||||
|
@ -201,24 +192,21 @@ offset: number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async watchForBottom(
|
async watchForBottom(already = false, fragment = new DocumentFragment()): Promise<boolean> {
|
||||||
already = false,
|
|
||||||
fragment = new DocumentFragment()
|
|
||||||
): Promise<boolean>{
|
|
||||||
let func: Function | undefined;
|
let func: Function | undefined;
|
||||||
if(!already) func = this.snapBottom();
|
if (!already) func = this.snapBottom();
|
||||||
if(!this.div)return false;
|
if (!this.div) return false;
|
||||||
try{
|
try {
|
||||||
let again = false;
|
let again = false;
|
||||||
const scrollBottom = this.scrollBottom;
|
const scrollBottom = this.scrollBottom;
|
||||||
if(scrollBottom < (already ? this.fillDist : this.minDist)){
|
if (scrollBottom < (already ? this.fillDist : this.minDist)) {
|
||||||
let nextid: string | undefined;
|
let nextid: string | undefined;
|
||||||
const lastelm = this.HTMLElements.at(-1);
|
const lastelm = this.HTMLElements.at(-1);
|
||||||
if(lastelm){
|
if (lastelm) {
|
||||||
const previd = lastelm[1];
|
const previd = lastelm[1];
|
||||||
nextid = await this.getIDFromOffset(previd, -1);
|
nextid = await this.getIDFromOffset(previd, -1);
|
||||||
}
|
}
|
||||||
if(nextid){
|
if (nextid) {
|
||||||
again = true;
|
again = true;
|
||||||
const html = await this.getHTMLFromID(nextid);
|
const html = await this.getHTMLFromID(nextid);
|
||||||
fragment.appendChild(html);
|
fragment.appendChild(html);
|
||||||
|
@ -226,57 +214,56 @@ offset: number
|
||||||
this.scrollBottom += this.averageheight;
|
this.scrollBottom += this.averageheight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(scrollBottom > this.maxDist){
|
if (scrollBottom > this.maxDist) {
|
||||||
const html = this.HTMLElements.pop();
|
const html = this.HTMLElements.pop();
|
||||||
if(html){
|
if (html) {
|
||||||
await this.destroyFromID(html[1]);
|
await this.destroyFromID(html[1]);
|
||||||
this.scrollBottom -= this.averageheight;
|
this.scrollBottom -= this.averageheight;
|
||||||
again = true;
|
again = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(again){
|
if (again) {
|
||||||
await this.watchForBottom(true, fragment);
|
await this.watchForBottom(true, fragment);
|
||||||
}
|
}
|
||||||
return again;
|
return again;
|
||||||
}finally{
|
} finally {
|
||||||
if(!already){
|
if (!already) {
|
||||||
this.div.append(fragment);
|
this.div.append(fragment);
|
||||||
if(func){
|
if (func) {
|
||||||
func();
|
func();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async watchForChange(): Promise<boolean>{
|
async watchForChange(): Promise<boolean> {
|
||||||
if(this.changePromise){
|
if (this.changePromise) {
|
||||||
this.watchtime = true;
|
this.watchtime = true;
|
||||||
return await this.changePromise;
|
return await this.changePromise;
|
||||||
}else{
|
} else {
|
||||||
this.watchtime = false;
|
this.watchtime = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.changePromise = new Promise<boolean>(async res=>{
|
this.changePromise = new Promise<boolean>(async (res) => {
|
||||||
try{
|
try {
|
||||||
if(!this.div){
|
if (!this.div) {
|
||||||
res(false);
|
res(false);
|
||||||
}
|
}
|
||||||
const out = (await Promise.allSettled([
|
const out = (await Promise.allSettled([this.watchForTop(), this.watchForBottom()])) as {
|
||||||
this.watchForTop(),
|
value: boolean;
|
||||||
this.watchForBottom(),
|
}[];
|
||||||
])) as { value: boolean }[];
|
|
||||||
const changed = out[0].value || out[1].value;
|
const changed = out[0].value || out[1].value;
|
||||||
if(this.timeout === null && changed){
|
if (this.timeout === null && changed) {
|
||||||
this.timeout = setTimeout(this.updatestuff.bind(this), 300);
|
this.timeout = setTimeout(this.updatestuff.bind(this), 300);
|
||||||
}
|
}
|
||||||
res(Boolean(changed));
|
res(Boolean(changed));
|
||||||
}catch(e){
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
res(false);
|
res(false);
|
||||||
}finally{
|
} finally {
|
||||||
setTimeout(()=>{
|
setTimeout(() => {
|
||||||
this.changePromise = undefined;
|
this.changePromise = undefined;
|
||||||
if(this.watchtime){
|
if (this.watchtime) {
|
||||||
this.watchForChange();
|
this.watchForChange();
|
||||||
}
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
|
@ -285,65 +272,65 @@ offset: number
|
||||||
|
|
||||||
return await this.changePromise;
|
return await this.changePromise;
|
||||||
}
|
}
|
||||||
async focus(id: string, flash = true): Promise<void>{
|
async focus(id: string, flash = true): Promise<void> {
|
||||||
let element: HTMLElement | undefined;
|
let element: HTMLElement | undefined;
|
||||||
for(const thing of this.HTMLElements){
|
for (const thing of this.HTMLElements) {
|
||||||
if(thing[1] === id){
|
if (thing[1] === id) {
|
||||||
element = thing[0];
|
element = thing[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(element){
|
if (element) {
|
||||||
if(flash){
|
if (flash) {
|
||||||
element.scrollIntoView({
|
element.scrollIntoView({
|
||||||
behavior: "smooth",
|
behavior: "smooth",
|
||||||
block: "center",
|
block: "center",
|
||||||
});
|
});
|
||||||
await new Promise(resolve=>{
|
await new Promise((resolve) => {
|
||||||
setTimeout(resolve, 1000);
|
setTimeout(resolve, 1000);
|
||||||
});
|
});
|
||||||
element.classList.remove("jumped");
|
element.classList.remove("jumped");
|
||||||
await new Promise(resolve=>{
|
await new Promise((resolve) => {
|
||||||
setTimeout(resolve, 100);
|
setTimeout(resolve, 100);
|
||||||
});
|
});
|
||||||
element.classList.add("jumped");
|
element.classList.add("jumped");
|
||||||
}else{
|
} else {
|
||||||
element.scrollIntoView();
|
element.scrollIntoView();
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
this.resetVars();
|
this.resetVars();
|
||||||
//TODO may be a redundant loop, not 100% sure :P
|
//TODO may be a redundant loop, not 100% sure :P
|
||||||
for(const thing of this.HTMLElements){
|
for (const thing of this.HTMLElements) {
|
||||||
await this.destroyFromID(thing[1]);
|
await this.destroyFromID(thing[1]);
|
||||||
}
|
}
|
||||||
this.HTMLElements = [];
|
this.HTMLElements = [];
|
||||||
await this.firstElement(id);
|
await this.firstElement(id);
|
||||||
this.updatestuff();
|
this.updatestuff();
|
||||||
await this.watchForChange();
|
await this.watchForChange();
|
||||||
await new Promise(resolve=>{
|
await new Promise((resolve) => {
|
||||||
setTimeout(resolve, 100);
|
setTimeout(resolve, 100);
|
||||||
});
|
});
|
||||||
await this.focus(id, true);
|
await this.focus(id, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(): Promise<void>{
|
async delete(): Promise<void> {
|
||||||
if(this.div){
|
if (this.div) {
|
||||||
this.div.remove();
|
this.div.remove();
|
||||||
this.div = null;
|
this.div = null;
|
||||||
}
|
}
|
||||||
this.resetVars();
|
this.resetVars();
|
||||||
try{
|
try {
|
||||||
for(const thing of this.HTMLElements){
|
for (const thing of this.HTMLElements) {
|
||||||
await this.destroyFromID(thing[1]);
|
await this.destroyFromID(thing[1]);
|
||||||
}
|
}
|
||||||
}catch(e){
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
this.HTMLElements = [];
|
this.HTMLElements = [];
|
||||||
if(this.timeout){
|
if (this.timeout) {
|
||||||
clearTimeout(this.timeout);
|
clearTimeout(this.timeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export{ InfiniteScroller };
|
export {InfiniteScroller};
|
||||||
|
|
|
@ -1,17 +1,26 @@
|
||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Jank Client</title>
|
<title>Jank Client</title>
|
||||||
<meta content="Invite" property="og:title">
|
<meta content="Invite" property="og:title" />
|
||||||
<meta content="Accept this invite for a spacebar guild" property="og:description">
|
<meta content="Accept this invite for a spacebar guild" property="og:description" />
|
||||||
<meta name="description" content="You shouldn't see this, but this is an invite URL">
|
<meta name="description" content="You shouldn't see this, but this is an invite URL" />
|
||||||
<meta content="/logo.webp" property="og:image">
|
<meta content="/logo.webp" property="og:image" />
|
||||||
<meta content="#4b458c" data-react-helmet="true" name="theme-color">
|
<meta content="#4b458c" data-react-helmet="true" name="theme-color" />
|
||||||
<link href="/style.css" rel="stylesheet">
|
<link href="/style.css" rel="stylesheet" />
|
||||||
<link href="/themes.css" rel="stylesheet" id="lightcss">
|
<link href="/themes.css" rel="stylesheet" id="lightcss" />
|
||||||
<style>body.no-theme{background:#16191b;}@media(prefers-color-scheme:light){body.no-theme{background:#9397bd;}}</style>
|
<style>
|
||||||
|
body.no-theme {
|
||||||
|
background: #16191b;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
body.no-theme {
|
||||||
|
background: #9397bd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="no-theme">
|
<body class="no-theme">
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -1,48 +1,47 @@
|
||||||
import { I18n } from "./i18n.js";
|
import {I18n} from "./i18n.js";
|
||||||
import{ getapiurls }from"./utils/utils.js";
|
import {getapiurls} from "./utils/utils.js";
|
||||||
import { getBulkUsers, Specialuser } from "./utils/utils.js";
|
import {getBulkUsers, Specialuser} from "./utils/utils.js";
|
||||||
|
|
||||||
(async ()=>{
|
(async () => {
|
||||||
const users = getBulkUsers();
|
const users = getBulkUsers();
|
||||||
const well = new URLSearchParams(window.location.search).get("instance");
|
const well = new URLSearchParams(window.location.search).get("instance");
|
||||||
const joinable: Specialuser[] = [];
|
const joinable: Specialuser[] = [];
|
||||||
|
|
||||||
for(const key in users.users){
|
for (const key in users.users) {
|
||||||
if(Object.prototype.hasOwnProperty.call(users.users, key)){
|
if (Object.prototype.hasOwnProperty.call(users.users, key)) {
|
||||||
const user: Specialuser = users.users[key];
|
const user: Specialuser = users.users[key];
|
||||||
if(well && user.serverurls.wellknown.includes(well)){
|
if (well && user.serverurls.wellknown.includes(well)) {
|
||||||
joinable.push(user);
|
joinable.push(user);
|
||||||
}
|
}
|
||||||
console.log(user);
|
console.log(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let urls: { api: string; cdn: string } | undefined;
|
let urls: {api: string; cdn: string} | undefined;
|
||||||
|
|
||||||
if(!joinable.length && well){
|
if (!joinable.length && well) {
|
||||||
const out = await getapiurls(well);
|
const out = await getapiurls(well);
|
||||||
if(out){
|
if (out) {
|
||||||
urls = out;
|
urls = out;
|
||||||
for(const key in users.users){
|
for (const key in users.users) {
|
||||||
if(Object.prototype.hasOwnProperty.call(users.users, key)){
|
if (Object.prototype.hasOwnProperty.call(users.users, key)) {
|
||||||
const user: Specialuser = users.users[key];
|
const user: Specialuser = users.users[key];
|
||||||
if(user.serverurls.api.includes(out.api)){
|
if (user.serverurls.api.includes(out.api)) {
|
||||||
joinable.push(user);
|
joinable.push(user);
|
||||||
}
|
}
|
||||||
console.log(user);
|
console.log(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
throw new Error(
|
throw new Error("Someone needs to handle the case where the servers don't exist");
|
||||||
"Someone needs to handle the case where the servers don't exist"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
urls = joinable[0].serverurls;
|
urls = joinable[0].serverurls;
|
||||||
}
|
}
|
||||||
await I18n.done;
|
await I18n.done;
|
||||||
if(!joinable.length){
|
if (!joinable.length) {
|
||||||
document.getElementById("AcceptInvite")!.textContent = I18n.getTranslation("htmlPages.noAccount");
|
document.getElementById("AcceptInvite")!.textContent =
|
||||||
|
I18n.getTranslation("htmlPages.noAccount");
|
||||||
}
|
}
|
||||||
|
|
||||||
const code = window.location.pathname.split("/")[2];
|
const code = window.location.pathname.split("/")[2];
|
||||||
|
@ -51,34 +50,36 @@ import { getBulkUsers, Specialuser } from "./utils/utils.js";
|
||||||
fetch(`${urls!.api}/invites/${code}`, {
|
fetch(`${urls!.api}/invites/${code}`, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
})
|
})
|
||||||
.then(response=>response.json())
|
.then((response) => response.json())
|
||||||
.then(json=>{
|
.then((json) => {
|
||||||
const guildjson = json.guild;
|
const guildjson = json.guild;
|
||||||
guildinfo = guildjson;
|
guildinfo = guildjson;
|
||||||
document.getElementById("invitename")!.textContent = guildjson.name;
|
document.getElementById("invitename")!.textContent = guildjson.name;
|
||||||
document.getElementById(
|
document.getElementById("invitedescription")!.textContent = I18n.getTranslation(
|
||||||
"invitedescription"
|
"invite.longInvitedBy",
|
||||||
)!.textContent = I18n.getTranslation("invite.longInvitedBy",json.inviter.username,guildjson.name)
|
json.inviter.username,
|
||||||
if(guildjson.icon){
|
guildjson.name,
|
||||||
|
);
|
||||||
|
if (guildjson.icon) {
|
||||||
const img = document.createElement("img");
|
const img = document.createElement("img");
|
||||||
img.src = `${urls!.cdn}/icons/${guildjson.id}/${guildjson.icon}.png`;
|
img.src = `${urls!.cdn}/icons/${guildjson.id}/${guildjson.icon}.png`;
|
||||||
img.classList.add("inviteGuild");
|
img.classList.add("inviteGuild");
|
||||||
document.getElementById("inviteimg")!.append(img);
|
document.getElementById("inviteimg")!.append(img);
|
||||||
}else{
|
} else {
|
||||||
const txt = guildjson.name
|
const txt = guildjson.name
|
||||||
.replace(/'s /g, " ")
|
.replace(/'s /g, " ")
|
||||||
.replace(/\w+/g, (word: any[])=>word[0])
|
.replace(/\w+/g, (word: any[]) => word[0])
|
||||||
.replace(/\s/g, "");
|
.replace(/\s/g, "");
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
div.textContent = txt;
|
div.textContent = txt;
|
||||||
div.classList.add("inviteGuild");
|
div.classList.add("inviteGuild");
|
||||||
document.getElementById("inviteimg")!.append(div);
|
document.getElementById("inviteimg")!.append(div);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function showAccounts(): void{
|
function showAccounts(): void {
|
||||||
const table = document.createElement("dialog");
|
const table = document.createElement("dialog");
|
||||||
for(const user of joinable){
|
for (const user of joinable) {
|
||||||
console.log(user.pfpsrc);
|
console.log(user.pfpsrc);
|
||||||
|
|
||||||
const userinfo = document.createElement("div");
|
const userinfo = document.createElement("div");
|
||||||
|
@ -95,23 +96,21 @@ document.getElementById("inviteimg")!.append(div);
|
||||||
userDiv.append(document.createElement("br"));
|
userDiv.append(document.createElement("br"));
|
||||||
|
|
||||||
const span = document.createElement("span");
|
const span = document.createElement("span");
|
||||||
span.textContent = user.serverurls.wellknown
|
span.textContent = user.serverurls.wellknown.replace("https://", "").replace("http://", "");
|
||||||
.replace("https://", "")
|
|
||||||
.replace("http://", "");
|
|
||||||
span.classList.add("serverURL");
|
span.classList.add("serverURL");
|
||||||
userDiv.append(span);
|
userDiv.append(span);
|
||||||
|
|
||||||
userinfo.append(userDiv);
|
userinfo.append(userDiv);
|
||||||
table.append(userinfo);
|
table.append(userinfo);
|
||||||
|
|
||||||
userinfo.addEventListener("click", ()=>{
|
userinfo.addEventListener("click", () => {
|
||||||
console.log(user);
|
console.log(user);
|
||||||
fetch(`${urls!.api}/invites/${code}`, {
|
fetch(`${urls!.api}/invites/${code}`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: user.token,
|
Authorization: user.token,
|
||||||
},
|
},
|
||||||
}).then(()=>{
|
}).then(() => {
|
||||||
users.currentuser = user.uid;
|
users.currentuser = user.uid;
|
||||||
localStorage.setItem("userinfos", JSON.stringify(users));
|
localStorage.setItem("userinfos", JSON.stringify(users));
|
||||||
window.location.href = "/channels/" + guildinfo.id;
|
window.location.href = "/channels/" + guildinfo.id;
|
||||||
|
@ -122,14 +121,14 @@ document.getElementById("inviteimg")!.append(div);
|
||||||
const td = document.createElement("div");
|
const td = document.createElement("div");
|
||||||
td.classList.add("switchtable");
|
td.classList.add("switchtable");
|
||||||
td.textContent = I18n.getTranslation("invite.loginOrCreateAccount");
|
td.textContent = I18n.getTranslation("invite.loginOrCreateAccount");
|
||||||
td.addEventListener("click", ()=>{
|
td.addEventListener("click", () => {
|
||||||
const l = new URLSearchParams("?");
|
const l = new URLSearchParams("?");
|
||||||
l.set("goback", window.location.href);
|
l.set("goback", window.location.href);
|
||||||
l.set("instance", well!);
|
l.set("instance", well!);
|
||||||
window.location.href = "/login?" + l.toString();
|
window.location.href = "/login?" + l.toString();
|
||||||
});
|
});
|
||||||
|
|
||||||
if(!joinable.length){
|
if (!joinable.length) {
|
||||||
const l = new URLSearchParams("?");
|
const l = new URLSearchParams("?");
|
||||||
l.set("goback", window.location.href);
|
l.set("goback", window.location.href);
|
||||||
l.set("instance", well!);
|
l.set("instance", well!);
|
||||||
|
@ -137,12 +136,10 @@ document.getElementById("inviteimg")!.append(div);
|
||||||
}
|
}
|
||||||
|
|
||||||
table.append(td);
|
table.append(td);
|
||||||
table.classList.add("flexttb","accountSwitcher");
|
table.classList.add("flexttb", "accountSwitcher");
|
||||||
console.log(table);
|
console.log(table);
|
||||||
document.body.append(table);
|
document.body.append(table);
|
||||||
}
|
}
|
||||||
|
|
||||||
document
|
document.getElementById("AcceptInvite")!.addEventListener("click", showAccounts);
|
||||||
.getElementById("AcceptInvite")!
|
|
||||||
.addEventListener("click", showAccounts);
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -63,7 +63,12 @@ type readyjson = {
|
||||||
};
|
};
|
||||||
user_guild_settings: {
|
user_guild_settings: {
|
||||||
entries: {
|
entries: {
|
||||||
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: number;
|
message_notifications: number;
|
||||||
flags: number;
|
flags: number;
|
||||||
hide_muted_channels: boolean;
|
hide_muted_channels: boolean;
|
||||||
|
@ -149,12 +154,12 @@ type memberjson = {
|
||||||
id: string;
|
id: string;
|
||||||
user: userjson | null;
|
user: userjson | null;
|
||||||
guild_id: string;
|
guild_id: string;
|
||||||
avatar?:string;
|
avatar?: string;
|
||||||
banner?:string;
|
banner?: string;
|
||||||
guild: {
|
guild: {
|
||||||
id: string;
|
id: string;
|
||||||
} | null;
|
} | null;
|
||||||
presence?:presencejson
|
presence?: presencejson;
|
||||||
nick?: string;
|
nick?: string;
|
||||||
roles: string[];
|
roles: string[];
|
||||||
joined_at: string;
|
joined_at: string;
|
||||||
|
@ -168,21 +173,20 @@ type emojijson = {
|
||||||
name: string;
|
name: string;
|
||||||
id?: string;
|
id?: string;
|
||||||
animated?: boolean;
|
animated?: boolean;
|
||||||
emoji?:string;
|
emoji?: string;
|
||||||
};
|
};
|
||||||
type emojipjson=emojijson&{
|
type emojipjson = emojijson & {
|
||||||
available: boolean,
|
available: boolean;
|
||||||
guild_id:string,
|
guild_id: string;
|
||||||
user_id:string,
|
user_id: string;
|
||||||
managed:boolean,
|
managed: boolean;
|
||||||
require_colons:boolean,
|
require_colons: boolean;
|
||||||
roles:string[],
|
roles: string[];
|
||||||
groups:null//TODO figure out what this means lol
|
groups: null; //TODO figure out what this means lol
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
type guildjson = {
|
type guildjson = {
|
||||||
application_command_counts: { [key: string]: number };
|
application_command_counts: {[key: string]: number};
|
||||||
channels: channeljson[];
|
channels: channeljson[];
|
||||||
data_mode: string;
|
data_mode: string;
|
||||||
emojis: emojipjson[];
|
emojis: emojipjson[];
|
||||||
|
@ -404,17 +408,18 @@ type messageCreateJson = {
|
||||||
s: number;
|
s: number;
|
||||||
t: "MESSAGE_CREATE";
|
t: "MESSAGE_CREATE";
|
||||||
};
|
};
|
||||||
type roleCreate={
|
type roleCreate = {
|
||||||
op: 0,
|
op: 0;
|
||||||
t: "GUILD_ROLE_CREATE",
|
t: "GUILD_ROLE_CREATE";
|
||||||
d: {
|
d: {
|
||||||
guild_id: string,
|
guild_id: string;
|
||||||
role: rolesjson
|
role: rolesjson;
|
||||||
},
|
};
|
||||||
s: 6
|
s: 6;
|
||||||
}
|
};
|
||||||
type wsjson =
|
type wsjson =
|
||||||
roleCreate | {
|
| roleCreate
|
||||||
|
| {
|
||||||
op: 0;
|
op: 0;
|
||||||
d: any;
|
d: any;
|
||||||
s: number;
|
s: number;
|
||||||
|
@ -429,13 +434,13 @@ roleCreate | {
|
||||||
| "MESSAGE_REACTION_REMOVE_ALL"
|
| "MESSAGE_REACTION_REMOVE_ALL"
|
||||||
| "MESSAGE_REACTION_REMOVE_EMOJI";
|
| "MESSAGE_REACTION_REMOVE_EMOJI";
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
op: 0;
|
op: 0;
|
||||||
t: "GUILD_MEMBERS_CHUNK";
|
t: "GUILD_MEMBERS_CHUNK";
|
||||||
d: memberChunk;
|
d: memberChunk;
|
||||||
s: number;
|
s: number;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
op: 0;
|
op: 0;
|
||||||
d: {
|
d: {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -444,8 +449,8 @@ roleCreate | {
|
||||||
};
|
};
|
||||||
s: number;
|
s: number;
|
||||||
t: "MESSAGE_DELETE";
|
t: "MESSAGE_DELETE";
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
op: 0;
|
op: 0;
|
||||||
d: {
|
d: {
|
||||||
guild_id?: string;
|
guild_id?: string;
|
||||||
|
@ -453,22 +458,22 @@ roleCreate | {
|
||||||
} & messagejson;
|
} & messagejson;
|
||||||
s: number;
|
s: number;
|
||||||
t: "MESSAGE_UPDATE";
|
t: "MESSAGE_UPDATE";
|
||||||
}
|
}
|
||||||
| messageCreateJson
|
| messageCreateJson
|
||||||
| readyjson
|
| readyjson
|
||||||
| {
|
| {
|
||||||
op: 11;
|
op: 11;
|
||||||
s: undefined;
|
s: undefined;
|
||||||
d: {};
|
d: {};
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
op: 10;
|
op: 10;
|
||||||
s: undefined;
|
s: undefined;
|
||||||
d: {
|
d: {
|
||||||
heartbeat_interval: number;
|
heartbeat_interval: number;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
op: 0;
|
op: 0;
|
||||||
t: "MESSAGE_REACTION_ADD";
|
t: "MESSAGE_REACTION_ADD";
|
||||||
d: {
|
d: {
|
||||||
|
@ -480,8 +485,8 @@ roleCreate | {
|
||||||
member?: memberjson;
|
member?: memberjson;
|
||||||
};
|
};
|
||||||
s: number;
|
s: number;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
op: 0;
|
op: 0;
|
||||||
t: "MESSAGE_REACTION_REMOVE";
|
t: "MESSAGE_REACTION_REMOVE";
|
||||||
d: {
|
d: {
|
||||||
|
@ -492,77 +497,89 @@ roleCreate | {
|
||||||
emoji: emojijson;
|
emoji: emojijson;
|
||||||
};
|
};
|
||||||
s: number;
|
s: number;
|
||||||
}|{
|
}
|
||||||
op: 0,
|
| {
|
||||||
t: "GUILD_ROLE_UPDATE",
|
op: 0;
|
||||||
|
t: "GUILD_ROLE_UPDATE";
|
||||||
d: {
|
d: {
|
||||||
guild_id: string,
|
guild_id: string;
|
||||||
role: rolesjson
|
role: rolesjson;
|
||||||
},
|
};
|
||||||
"s": number
|
s: number;
|
||||||
}|{
|
}
|
||||||
op: 0,
|
| {
|
||||||
t: "GUILD_ROLE_DELETE",
|
op: 0;
|
||||||
|
t: "GUILD_ROLE_DELETE";
|
||||||
d: {
|
d: {
|
||||||
guild_id: string,
|
guild_id: string;
|
||||||
role_id: string
|
role_id: string;
|
||||||
},
|
};
|
||||||
s:number
|
s: number;
|
||||||
}|{
|
}
|
||||||
op: 0,
|
| {
|
||||||
t: "GUILD_MEMBER_UPDATE",
|
op: 0;
|
||||||
d: memberjson,
|
t: "GUILD_MEMBER_UPDATE";
|
||||||
s: 3
|
d: memberjson;
|
||||||
}|{
|
s: 3;
|
||||||
op:9,
|
}
|
||||||
d:boolean,
|
| {
|
||||||
s:number
|
op: 9;
|
||||||
}|memberlistupdatejson|voiceupdate|voiceserverupdate|{
|
d: boolean;
|
||||||
op: 0,
|
s: number;
|
||||||
t: "RELATIONSHIP_ADD",
|
}
|
||||||
|
| memberlistupdatejson
|
||||||
|
| voiceupdate
|
||||||
|
| voiceserverupdate
|
||||||
|
| {
|
||||||
|
op: 0;
|
||||||
|
t: "RELATIONSHIP_ADD";
|
||||||
d: {
|
d: {
|
||||||
id: string,
|
id: string;
|
||||||
type: 0|1|2|3|4|5|6,
|
type: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||||
user: userjson
|
user: userjson;
|
||||||
},
|
};
|
||||||
s: number
|
s: number;
|
||||||
}|{
|
}
|
||||||
op: 0,
|
| {
|
||||||
t: "RELATIONSHIP_REMOVE",
|
op: 0;
|
||||||
|
t: "RELATIONSHIP_REMOVE";
|
||||||
d: {
|
d: {
|
||||||
id: string,
|
id: string;
|
||||||
type: number,
|
type: number;
|
||||||
nickname: null
|
nickname: null;
|
||||||
},
|
};
|
||||||
s: number
|
s: number;
|
||||||
}|{
|
}
|
||||||
op: 0,
|
| {
|
||||||
t: "PRESENCE_UPDATE",
|
op: 0;
|
||||||
d: presencejson,
|
t: "PRESENCE_UPDATE";
|
||||||
s:number
|
d: presencejson;
|
||||||
}|{
|
s: number;
|
||||||
op:0,
|
}
|
||||||
t:"GUILD_MEMBER_ADD",
|
| {
|
||||||
d:memberjson,
|
op: 0;
|
||||||
s:number
|
t: "GUILD_MEMBER_ADD";
|
||||||
}|{
|
d: memberjson;
|
||||||
op:0,
|
s: number;
|
||||||
t:"GUILD_MEMBER_REMOVE",
|
}
|
||||||
d:{
|
| {
|
||||||
guild_id:string,
|
op: 0;
|
||||||
user:userjson
|
t: "GUILD_MEMBER_REMOVE";
|
||||||
},
|
|
||||||
s:number
|
|
||||||
}|{
|
|
||||||
op: 0,
|
|
||||||
t: "GUILD_EMOJIS_UPDATE",
|
|
||||||
d: {
|
d: {
|
||||||
guild_id: string,
|
guild_id: string;
|
||||||
emojis: emojipjson[]
|
user: userjson;
|
||||||
},
|
};
|
||||||
s: number
|
s: number;
|
||||||
};
|
}
|
||||||
|
| {
|
||||||
|
op: 0;
|
||||||
|
t: "GUILD_EMOJIS_UPDATE";
|
||||||
|
d: {
|
||||||
|
guild_id: string;
|
||||||
|
emojis: emojipjson[];
|
||||||
|
};
|
||||||
|
s: number;
|
||||||
|
};
|
||||||
|
|
||||||
type memberChunk = {
|
type memberChunk = {
|
||||||
guild_id: string;
|
guild_id: string;
|
||||||
|
@ -573,134 +590,140 @@ type memberChunk = {
|
||||||
chunk_count: number;
|
chunk_count: number;
|
||||||
not_found: string[];
|
not_found: string[];
|
||||||
};
|
};
|
||||||
type voiceupdate={
|
type voiceupdate = {
|
||||||
op: 0,
|
op: 0;
|
||||||
t: "VOICE_STATE_UPDATE",
|
t: "VOICE_STATE_UPDATE";
|
||||||
d: {
|
d: {
|
||||||
guild_id: string,
|
guild_id: string;
|
||||||
channel_id: string,
|
channel_id: string;
|
||||||
user_id: string,
|
user_id: string;
|
||||||
member: memberjson,
|
member: memberjson;
|
||||||
session_id: string,
|
session_id: string;
|
||||||
token: string,
|
token: string;
|
||||||
deaf: boolean,
|
deaf: boolean;
|
||||||
mute: boolean,
|
mute: boolean;
|
||||||
self_deaf: boolean,
|
self_deaf: boolean;
|
||||||
self_mute: boolean,
|
self_mute: boolean;
|
||||||
self_video: boolean,
|
self_video: boolean;
|
||||||
suppress: boolean
|
suppress: boolean;
|
||||||
},
|
};
|
||||||
s: number
|
s: number;
|
||||||
};
|
};
|
||||||
type voiceserverupdate={
|
type voiceserverupdate = {
|
||||||
op: 0,
|
op: 0;
|
||||||
t: "VOICE_SERVER_UPDATE",
|
t: "VOICE_SERVER_UPDATE";
|
||||||
d: {
|
d: {
|
||||||
token: string,
|
token: string;
|
||||||
guild_id: string,
|
guild_id: string;
|
||||||
endpoint: string
|
endpoint: string;
|
||||||
},
|
};
|
||||||
s: 6
|
s: 6;
|
||||||
};
|
};
|
||||||
type memberlistupdatejson={
|
type memberlistupdatejson = {
|
||||||
op: 0,
|
op: 0;
|
||||||
s: number,
|
s: number;
|
||||||
t: "GUILD_MEMBER_LIST_UPDATE",
|
t: "GUILD_MEMBER_LIST_UPDATE";
|
||||||
d: {
|
d: {
|
||||||
ops: [
|
ops: [
|
||||||
{
|
{
|
||||||
items:({
|
items: (
|
||||||
group:{
|
| {
|
||||||
count:number,
|
group: {
|
||||||
id:string
|
count: number;
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}|{
|
| {
|
||||||
member:memberjson
|
member: memberjson;
|
||||||
})[]
|
|
||||||
op: "SYNC",
|
|
||||||
range: [
|
|
||||||
number,
|
|
||||||
number
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
)[];
|
||||||
online_count: number,
|
op: "SYNC";
|
||||||
member_count: number,
|
range: [number, number];
|
||||||
id: string,
|
},
|
||||||
guild_id: string,
|
];
|
||||||
|
online_count: number;
|
||||||
|
member_count: number;
|
||||||
|
id: string;
|
||||||
|
guild_id: string;
|
||||||
groups: {
|
groups: {
|
||||||
count: number,
|
count: number;
|
||||||
id: string
|
id: string;
|
||||||
}[]
|
}[];
|
||||||
}
|
};
|
||||||
}
|
|
||||||
type webRTCSocket= {
|
|
||||||
op: 8,
|
|
||||||
d: {
|
|
||||||
heartbeat_interval: number
|
|
||||||
}
|
|
||||||
}|{
|
|
||||||
op:6,
|
|
||||||
d:{t: number}
|
|
||||||
}|{
|
|
||||||
op: 2,
|
|
||||||
d: {
|
|
||||||
ssrc: number,
|
|
||||||
"streams": {
|
|
||||||
type: "video",//probally more options, but idk
|
|
||||||
rid: string,
|
|
||||||
quality: number,
|
|
||||||
ssrc: number,
|
|
||||||
rtx_ssrc:number
|
|
||||||
}[],
|
|
||||||
ip: number,
|
|
||||||
port: number,
|
|
||||||
"modes": [],//no clue
|
|
||||||
"experiments": []//no clue
|
|
||||||
}
|
|
||||||
}|sdpback|opRTC12|{
|
|
||||||
op: 5,
|
|
||||||
d: {
|
|
||||||
user_id: string,
|
|
||||||
speaking: 0,
|
|
||||||
ssrc: 940464811
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
type sdpback={
|
type webRTCSocket =
|
||||||
op: 4,
|
| {
|
||||||
|
op: 8;
|
||||||
d: {
|
d: {
|
||||||
audioCodec: string,
|
heartbeat_interval: number;
|
||||||
videoCodec: string,
|
};
|
||||||
media_session_id: string,
|
|
||||||
sdp: string
|
|
||||||
}
|
}
|
||||||
};
|
| {
|
||||||
type opRTC12={
|
op: 6;
|
||||||
op: 12,
|
d: {t: number};
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
op: 2;
|
||||||
d: {
|
d: {
|
||||||
user_id: string,
|
ssrc: number;
|
||||||
audio_ssrc: number,
|
streams: {
|
||||||
video_ssrc: number,
|
type: "video"; //probally more options, but idk
|
||||||
|
rid: string;
|
||||||
|
quality: number;
|
||||||
|
ssrc: number;
|
||||||
|
rtx_ssrc: number;
|
||||||
|
}[];
|
||||||
|
ip: number;
|
||||||
|
port: number;
|
||||||
|
modes: []; //no clue
|
||||||
|
experiments: []; //no clue
|
||||||
|
};
|
||||||
|
}
|
||||||
|
| sdpback
|
||||||
|
| opRTC12
|
||||||
|
| {
|
||||||
|
op: 5;
|
||||||
|
d: {
|
||||||
|
user_id: string;
|
||||||
|
speaking: 0;
|
||||||
|
ssrc: 940464811;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
type sdpback = {
|
||||||
|
op: 4;
|
||||||
|
d: {
|
||||||
|
audioCodec: string;
|
||||||
|
videoCodec: string;
|
||||||
|
media_session_id: string;
|
||||||
|
sdp: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
type opRTC12 = {
|
||||||
|
op: 12;
|
||||||
|
d: {
|
||||||
|
user_id: string;
|
||||||
|
audio_ssrc: number;
|
||||||
|
video_ssrc: number;
|
||||||
streams: [
|
streams: [
|
||||||
{
|
{
|
||||||
type: "video",
|
type: "video";
|
||||||
rid: "100",
|
rid: "100";
|
||||||
ssrc: number,
|
ssrc: number;
|
||||||
active: boolean,
|
active: boolean;
|
||||||
quality: 100,
|
quality: 100;
|
||||||
rtx_ssrc: number,
|
rtx_ssrc: number;
|
||||||
max_bitrate: 2500000,
|
max_bitrate: 2500000;
|
||||||
max_framerate: number,
|
max_framerate: number;
|
||||||
max_resolution: {
|
max_resolution: {
|
||||||
type: "fixed",
|
type: "fixed";
|
||||||
width: number,
|
width: number;
|
||||||
height: number
|
height: number;
|
||||||
}
|
};
|
||||||
}
|
},
|
||||||
]
|
];
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
export{
|
export {
|
||||||
readyjson,
|
readyjson,
|
||||||
dirrectjson,
|
dirrectjson,
|
||||||
startTypingjson,
|
startTypingjson,
|
||||||
|
@ -725,5 +748,5 @@ export{
|
||||||
webRTCSocket,
|
webRTCSocket,
|
||||||
sdpback,
|
sdpback,
|
||||||
opRTC12,
|
opRTC12,
|
||||||
emojipjson
|
emojipjson,
|
||||||
};
|
};
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,19 +1,25 @@
|
||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Jank Client</title>
|
<title>Jank Client</title>
|
||||||
<meta content="Jank Client" property="og:title">
|
<meta content="Jank Client" property="og:title" />
|
||||||
<meta
|
<meta content="A spacebar client that has DMs, replying and more" property="og:description" />
|
||||||
content="A spacebar client that has DMs, replying and more"
|
<meta content="/logo.webp" property="og:image" />
|
||||||
property="og:description"
|
<meta content="#4b458c" data-react-helmet="true" name="theme-color" />
|
||||||
>
|
<link href="/style.css" rel="stylesheet" />
|
||||||
<meta content="/logo.webp" property="og:image">
|
<link href="/themes.css" rel="stylesheet" id="lightcss" />
|
||||||
<meta content="#4b458c" data-react-helmet="true" name="theme-color">
|
<style>
|
||||||
<link href="/style.css" rel="stylesheet">
|
body.no-theme {
|
||||||
<link href="/themes.css" rel="stylesheet" id="lightcss">
|
background: #16191b;
|
||||||
<style>body.no-theme{background:#16191b;}@media(prefers-color-scheme:light){body.no-theme{background:#9397bd;}}</style>
|
}
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
body.no-theme {
|
||||||
|
background: #9397bd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="no-theme">
|
<body class="no-theme">
|
||||||
<div id="logindiv">
|
<div id="logindiv">
|
||||||
|
@ -29,25 +35,13 @@
|
||||||
id="instancein"
|
id="instancein"
|
||||||
value=""
|
value=""
|
||||||
required
|
required
|
||||||
>
|
/>
|
||||||
|
|
||||||
<label for="uname" id="emailField"><b>Email:</b></label>
|
<label for="uname" id="emailField"><b>Email:</b></label>
|
||||||
<input
|
<input type="text" placeholder="Enter email address" name="uname" id="uname" required />
|
||||||
type="text"
|
|
||||||
placeholder="Enter email address"
|
|
||||||
name="uname"
|
|
||||||
id="uname"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
|
|
||||||
<label for="psw" id="pwField"><b>Password:</b></label>
|
<label for="psw" id="pwField"><b>Password:</b></label>
|
||||||
<input
|
<input type="password" placeholder="Enter Password" name="psw" id="psw" required />
|
||||||
type="password"
|
|
||||||
placeholder="Enter Password"
|
|
||||||
name="psw"
|
|
||||||
id="psw"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<p class="wrongred" id="wrong"></p>
|
<p class="wrongred" id="wrong"></p>
|
||||||
|
|
||||||
<div id="h-captcha"></div>
|
<div id="h-captcha"></div>
|
||||||
|
|
|
@ -1,55 +1,50 @@
|
||||||
import { getBulkInfo, Specialuser } from "./utils/utils.js";
|
import {getBulkInfo, Specialuser} from "./utils/utils.js";
|
||||||
import { I18n } from "./i18n.js";
|
import {I18n} from "./i18n.js";
|
||||||
import { Dialog, FormError } from "./settings.js";
|
import {Dialog, FormError} from "./settings.js";
|
||||||
import { checkInstance } from "./utils/utils.js";
|
import {checkInstance} from "./utils/utils.js";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
await I18n.done;
|
await I18n.done;
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
await I18n.done;
|
||||||
(async ()=>{
|
const instanceField = document.getElementById("instanceField");
|
||||||
await I18n.done
|
const emailField = document.getElementById("emailField");
|
||||||
const instanceField=document.getElementById("instanceField");
|
const pwField = document.getElementById("pwField");
|
||||||
const emailField= document.getElementById("emailField");
|
const loginButton = document.getElementById("loginButton");
|
||||||
const pwField= document.getElementById("pwField");
|
const noAccount = document.getElementById("switch");
|
||||||
const loginButton=document.getElementById("loginButton");
|
if (instanceField && emailField && pwField && loginButton && noAccount) {
|
||||||
const noAccount=document.getElementById("switch")
|
instanceField.textContent = I18n.getTranslation("htmlPages.instanceField");
|
||||||
if(instanceField&&emailField&&pwField&&loginButton&&noAccount){
|
emailField.textContent = I18n.getTranslation("htmlPages.emailField");
|
||||||
instanceField.textContent=I18n.getTranslation("htmlPages.instanceField");
|
pwField.textContent = I18n.getTranslation("htmlPages.pwField");
|
||||||
emailField.textContent=I18n.getTranslation("htmlPages.emailField");
|
loginButton.textContent = I18n.getTranslation("htmlPages.loginButton");
|
||||||
pwField.textContent=I18n.getTranslation("htmlPages.pwField");
|
noAccount.textContent = I18n.getTranslation("htmlPages.noAccount");
|
||||||
loginButton.textContent=I18n.getTranslation("htmlPages.loginButton");
|
|
||||||
noAccount.textContent=I18n.getTranslation("htmlPages.noAccount");
|
|
||||||
}
|
}
|
||||||
})()
|
})();
|
||||||
|
|
||||||
|
function trimswitcher() {
|
||||||
function trimswitcher(){
|
|
||||||
const json = getBulkInfo();
|
const json = getBulkInfo();
|
||||||
const map = new Map();
|
const map = new Map();
|
||||||
for(const thing in json.users){
|
for (const thing in json.users) {
|
||||||
const user = json.users[thing];
|
const user = json.users[thing];
|
||||||
let wellknown = user.serverurls.wellknown;
|
let wellknown = user.serverurls.wellknown;
|
||||||
if(wellknown.at(-1) !== "/"){
|
if (wellknown.at(-1) !== "/") {
|
||||||
wellknown += "/";
|
wellknown += "/";
|
||||||
}
|
}
|
||||||
wellknown =(user.id||user.email)+"@"+wellknown;
|
wellknown = (user.id || user.email) + "@" + wellknown;
|
||||||
if(map.has(wellknown)){
|
if (map.has(wellknown)) {
|
||||||
const otheruser = map.get(wellknown);
|
const otheruser = map.get(wellknown);
|
||||||
if(otheruser[1].serverurls.wellknown.at(-1) === "/"){
|
if (otheruser[1].serverurls.wellknown.at(-1) === "/") {
|
||||||
delete json.users[otheruser[0]];
|
delete json.users[otheruser[0]];
|
||||||
map.set(wellknown, [thing, user]);
|
map.set(wellknown, [thing, user]);
|
||||||
}else{
|
} else {
|
||||||
delete json.users[thing];
|
delete json.users[thing];
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
map.set(wellknown, [thing, user]);
|
map.set(wellknown, [thing, user]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(const thing in json.users){
|
for (const thing in json.users) {
|
||||||
if(thing.at(-1) === "/"){
|
if (thing.at(-1) === "/") {
|
||||||
const user = json.users[thing];
|
const user = json.users[thing];
|
||||||
delete json.users[thing];
|
delete json.users[thing];
|
||||||
json.users[thing.slice(0, -1)] = user;
|
json.users[thing.slice(0, -1)] = user;
|
||||||
|
@ -59,9 +54,7 @@ function trimswitcher(){
|
||||||
console.log(json);
|
console.log(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function adduser(user: typeof Specialuser.prototype.json) {
|
||||||
|
|
||||||
function adduser(user: typeof Specialuser.prototype.json){
|
|
||||||
user = new Specialuser(user);
|
user = new Specialuser(user);
|
||||||
const info = getBulkInfo();
|
const info = getBulkInfo();
|
||||||
info.users[user.uid] = user;
|
info.users[user.uid] = user;
|
||||||
|
@ -73,32 +66,30 @@ const instancein = document.getElementById("instancein") as HTMLInputElement;
|
||||||
let timeout: ReturnType<typeof setTimeout> | string | number | undefined | null = null;
|
let timeout: ReturnType<typeof setTimeout> | string | number | undefined | null = null;
|
||||||
// let instanceinfo;
|
// let instanceinfo;
|
||||||
|
|
||||||
|
if (instancein) {
|
||||||
|
|
||||||
if(instancein){
|
|
||||||
console.log(instancein);
|
console.log(instancein);
|
||||||
instancein.addEventListener("keydown", ()=>{
|
instancein.addEventListener("keydown", () => {
|
||||||
const verify = document.getElementById("verify");
|
const verify = document.getElementById("verify");
|
||||||
verify!.textContent = I18n.getTranslation("login.waiting");
|
verify!.textContent = I18n.getTranslation("login.waiting");
|
||||||
if(timeout !== null && typeof timeout !== "string"){
|
if (timeout !== null && typeof timeout !== "string") {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
}
|
}
|
||||||
timeout = setTimeout(()=>checkInstance((instancein as HTMLInputElement).value), 1000);
|
timeout = setTimeout(() => checkInstance((instancein as HTMLInputElement).value), 1000);
|
||||||
});
|
});
|
||||||
if(localStorage.getItem("instanceinfo")){
|
if (localStorage.getItem("instanceinfo")) {
|
||||||
const json = JSON.parse(localStorage.getItem("instanceinfo")!);
|
const json = JSON.parse(localStorage.getItem("instanceinfo")!);
|
||||||
if(json.value){
|
if (json.value) {
|
||||||
(instancein as HTMLInputElement).value = json.value;
|
(instancein as HTMLInputElement).value = json.value;
|
||||||
}else{
|
} else {
|
||||||
(instancein as HTMLInputElement).value = json.wellknown;
|
(instancein as HTMLInputElement).value = json.wellknown;
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
checkInstance("https://spacebar.chat/");
|
checkInstance("https://spacebar.chat/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function login(username: string, password: string, captcha: string){
|
async function login(username: string, password: string, captcha: string) {
|
||||||
if(captcha === ""){
|
if (captcha === "") {
|
||||||
captcha = "";
|
captcha = "";
|
||||||
}
|
}
|
||||||
const options = {
|
const options = {
|
||||||
|
@ -113,25 +104,25 @@ async function login(username: string, password: string, captcha: string){
|
||||||
"Content-type": "application/json; charset=UTF-8",
|
"Content-type": "application/json; charset=UTF-8",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
try{
|
try {
|
||||||
const info = JSON.parse(localStorage.getItem("instanceinfo")!);
|
const info = JSON.parse(localStorage.getItem("instanceinfo")!);
|
||||||
const api = info.login + (info.login.startsWith("/") ? "/" : "");
|
const api = info.login + (info.login.startsWith("/") ? "/" : "");
|
||||||
return await fetch(api + "/auth/login", options)
|
return await fetch(api + "/auth/login", options)
|
||||||
.then(response=>response.json())
|
.then((response) => response.json())
|
||||||
.then(response=>{
|
.then((response) => {
|
||||||
console.log(response, response.message);
|
console.log(response, response.message);
|
||||||
if(response.message === "Invalid Form Body"){
|
if (response.message === "Invalid Form Body") {
|
||||||
return response.errors.login._errors[0].message;
|
return response.errors.login._errors[0].message;
|
||||||
console.log("test");
|
console.log("test");
|
||||||
}
|
}
|
||||||
//this.serverurls||!this.email||!this.token
|
//this.serverurls||!this.email||!this.token
|
||||||
console.log(response);
|
console.log(response);
|
||||||
|
|
||||||
if(response.captcha_sitekey){
|
if (response.captcha_sitekey) {
|
||||||
const capt = document.getElementById("h-captcha");
|
const capt = document.getElementById("h-captcha");
|
||||||
if(capt!.children.length){
|
if (capt!.children.length) {
|
||||||
eval("hcaptcha.reset()");
|
eval("hcaptcha.reset()");
|
||||||
}else{
|
} else {
|
||||||
const capty = document.createElement("div");
|
const capty = document.createElement("div");
|
||||||
capty.classList.add("h-captcha");
|
capty.classList.add("h-captcha");
|
||||||
|
|
||||||
|
@ -141,107 +132,100 @@ async function login(username: string, password: string, captcha: string){
|
||||||
capt!.append(script);
|
capt!.append(script);
|
||||||
capt!.append(capty);
|
capt!.append(capty);
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
console.log(response);
|
console.log(response);
|
||||||
if(response.ticket){
|
if (response.ticket) {
|
||||||
const better=new Dialog("");
|
const better = new Dialog("");
|
||||||
const form=better.options.addForm("",(res:any)=>{
|
const form = better.options.addForm(
|
||||||
if(res.message){
|
"",
|
||||||
throw new FormError(ti,res.message);
|
(res: any) => {
|
||||||
}else{
|
if (res.message) {
|
||||||
|
throw new FormError(ti, res.message);
|
||||||
|
} else {
|
||||||
console.warn(res);
|
console.warn(res);
|
||||||
if(!res.token)return;
|
if (!res.token) return;
|
||||||
adduser({
|
adduser({
|
||||||
serverurls: JSON.parse(localStorage.getItem("instanceinfo") as string),
|
serverurls: JSON.parse(localStorage.getItem("instanceinfo") as string),
|
||||||
email: username,
|
email: username,
|
||||||
token: res.token,
|
token: res.token,
|
||||||
}).username = username;
|
}).username = username;
|
||||||
const redir = new URLSearchParams(
|
const redir = new URLSearchParams(window.location.search).get("goback");
|
||||||
window.location.search
|
if (redir) {
|
||||||
).get("goback");
|
|
||||||
if(redir){
|
|
||||||
window.location.href = redir;
|
window.location.href = redir;
|
||||||
}else{
|
} else {
|
||||||
window.location.href = "/channels/@me";
|
window.location.href = "/channels/@me";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},{
|
},
|
||||||
fetchURL:api + "/auth/mfa/totp",
|
{
|
||||||
method:"POST",
|
fetchURL: api + "/auth/mfa/totp",
|
||||||
headers:{
|
method: "POST",
|
||||||
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
}
|
},
|
||||||
});
|
},
|
||||||
|
);
|
||||||
form.addTitle(I18n.getTranslation("2faCode"));
|
form.addTitle(I18n.getTranslation("2faCode"));
|
||||||
const ti=form.addTextInput("","code");
|
const ti = form.addTextInput("", "code");
|
||||||
better.show()
|
better.show();
|
||||||
}else{
|
} else {
|
||||||
console.warn(response);
|
console.warn(response);
|
||||||
if(!response.token)return;
|
if (!response.token) return;
|
||||||
adduser({
|
adduser({
|
||||||
serverurls: JSON.parse(localStorage.getItem("instanceinfo")!),
|
serverurls: JSON.parse(localStorage.getItem("instanceinfo")!),
|
||||||
email: username,
|
email: username,
|
||||||
token: response.token,
|
token: response.token,
|
||||||
}).username = username;
|
}).username = username;
|
||||||
const redir = new URLSearchParams(window.location.search).get(
|
const redir = new URLSearchParams(window.location.search).get("goback");
|
||||||
"goback"
|
if (redir) {
|
||||||
);
|
|
||||||
if(redir){
|
|
||||||
window.location.href = redir;
|
window.location.href = redir;
|
||||||
}else{
|
} else {
|
||||||
window.location.href = "/channels/@me";
|
window.location.href = "/channels/@me";
|
||||||
}
|
}
|
||||||
return"";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}catch(error){
|
} catch (error) {
|
||||||
console.error("Error:", error);
|
console.error("Error:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function check(e: SubmitEvent){
|
async function check(e: SubmitEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const target = e.target as HTMLFormElement;
|
const target = e.target as HTMLFormElement;
|
||||||
const h = await login(
|
const h = await login(
|
||||||
(target[1] as HTMLInputElement).value,
|
(target[1] as HTMLInputElement).value,
|
||||||
(target[2] as HTMLInputElement).value,
|
(target[2] as HTMLInputElement).value,
|
||||||
(target[3] as HTMLInputElement).value
|
(target[3] as HTMLInputElement).value,
|
||||||
);
|
);
|
||||||
const wrongElement = document.getElementById("wrong");
|
const wrongElement = document.getElementById("wrong");
|
||||||
if(wrongElement){
|
if (wrongElement) {
|
||||||
wrongElement.textContent = h;
|
wrongElement.textContent = h;
|
||||||
}
|
}
|
||||||
console.log(h);
|
console.log(h);
|
||||||
}
|
}
|
||||||
if(document.getElementById("form")){
|
if (document.getElementById("form")) {
|
||||||
const form = document.getElementById("form");
|
const form = document.getElementById("form");
|
||||||
if(form){
|
if (form) {
|
||||||
form.addEventListener("submit", (e: SubmitEvent)=>check(e));
|
form.addEventListener("submit", (e: SubmitEvent) => check(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//this currently does not work, and need to be implemented better at some time.
|
//this currently does not work, and need to be implemented better at some time.
|
||||||
if(!localStorage.getItem("SWMode")){
|
if (!localStorage.getItem("SWMode")) {
|
||||||
localStorage.setItem("SWMode","true");
|
localStorage.setItem("SWMode", "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
const switchurl = document.getElementById("switch") as HTMLAreaElement;
|
const switchurl = document.getElementById("switch") as HTMLAreaElement;
|
||||||
if(switchurl){
|
if (switchurl) {
|
||||||
switchurl.href += window.location.search;
|
switchurl.href += window.location.search;
|
||||||
const instance = new URLSearchParams(window.location.search).get("instance");
|
const instance = new URLSearchParams(window.location.search).get("instance");
|
||||||
console.log(instance);
|
console.log(instance);
|
||||||
if(instance){
|
if (instance) {
|
||||||
instancein.value = instance;
|
instancein.value = instance;
|
||||||
checkInstance("");
|
checkInstance("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trimswitcher();
|
trimswitcher();
|
||||||
|
|
||||||
export{
|
export {adduser};
|
||||||
adduser,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,125 +1,127 @@
|
||||||
import{ User }from"./user.js";
|
import {User} from "./user.js";
|
||||||
import{ Role }from"./role.js";
|
import {Role} from "./role.js";
|
||||||
import{ Guild }from"./guild.js";
|
import {Guild} from "./guild.js";
|
||||||
import{ SnowFlake }from"./snowflake.js";
|
import {SnowFlake} from "./snowflake.js";
|
||||||
import{ memberjson, presencejson }from"./jsontypes.js";
|
import {memberjson, presencejson} from "./jsontypes.js";
|
||||||
import { I18n } from "./i18n.js";
|
import {I18n} from "./i18n.js";
|
||||||
import { Dialog, Options, Settings } from "./settings.js";
|
import {Dialog, Options, Settings} from "./settings.js";
|
||||||
|
|
||||||
class Member extends SnowFlake{
|
class Member extends SnowFlake {
|
||||||
static already = {};
|
static already = {};
|
||||||
owner: Guild;
|
owner: Guild;
|
||||||
user: User;
|
user: User;
|
||||||
roles: Role[] = [];
|
roles: Role[] = [];
|
||||||
nick!: string;
|
nick!: string;
|
||||||
avatar:void|string=undefined;
|
avatar: void | string = undefined;
|
||||||
banner:void|string=undefined;
|
banner: void | string = undefined;
|
||||||
private constructor(memberjson: memberjson, owner: Guild){
|
private constructor(memberjson: memberjson, owner: Guild) {
|
||||||
super(memberjson.id);
|
super(memberjson.id);
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
if(this.localuser.userMap.has(memberjson.id)){
|
if (this.localuser.userMap.has(memberjson.id)) {
|
||||||
this.user = this.localuser.userMap.get(memberjson.id) as User;
|
this.user = this.localuser.userMap.get(memberjson.id) as User;
|
||||||
}else if(memberjson.user){
|
} else if (memberjson.user) {
|
||||||
this.user = new User(memberjson.user, owner.localuser);
|
this.user = new User(memberjson.user, owner.localuser);
|
||||||
}else{
|
} else {
|
||||||
throw new Error("Missing user object of this member");
|
throw new Error("Missing user object of this member");
|
||||||
}
|
}
|
||||||
if(this.localuser.userMap.has(this?.id)){
|
if (this.localuser.userMap.has(this?.id)) {
|
||||||
this.user = this.localuser.userMap.get(this?.id) as User;
|
this.user = this.localuser.userMap.get(this?.id) as User;
|
||||||
}
|
}
|
||||||
for(const key of Object.keys(memberjson)){
|
for (const key of Object.keys(memberjson)) {
|
||||||
if(key === "guild" || key === "owner" || key === "user"){
|
if (key === "guild" || key === "owner" || key === "user") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(key === "roles"){
|
if (key === "roles") {
|
||||||
for(const strrole of memberjson.roles){
|
for (const strrole of memberjson.roles) {
|
||||||
const role = this.guild.roleids.get(strrole);
|
const role = this.guild.roleids.get(strrole);
|
||||||
if(!role)continue;
|
if (!role) continue;
|
||||||
this.roles.push(role);
|
this.roles.push(role);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if(key === "presence"){
|
if (key === "presence") {
|
||||||
this.getPresence(memberjson.presence);
|
this.getPresence(memberjson.presence);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
(this as any)[key] = (memberjson as any)[key];
|
(this as any)[key] = (memberjson as any)[key];
|
||||||
}
|
}
|
||||||
if(!this.user.bot){
|
if (!this.user.bot) {
|
||||||
const everyone=this.guild.roleids.get(this.guild.id);
|
const everyone = this.guild.roleids.get(this.guild.id);
|
||||||
if(everyone&&(this.roles.indexOf(everyone)===-1)){
|
if (everyone && this.roles.indexOf(everyone) === -1) {
|
||||||
this.roles.push(everyone)
|
this.roles.push(everyone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.roles.sort((a, b)=>{
|
this.roles.sort((a, b) => {
|
||||||
return this.guild.roles.indexOf(a) - this.guild.roles.indexOf(b);
|
return this.guild.roles.indexOf(a) - this.guild.roles.indexOf(b);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
remove(){
|
remove() {
|
||||||
this.user.members.delete(this.guild);
|
this.user.members.delete(this.guild);
|
||||||
this.guild.members.delete(this);
|
this.guild.members.delete(this);
|
||||||
}
|
}
|
||||||
getpfpsrc():string{
|
getpfpsrc(): string {
|
||||||
if(this.hypotheticalpfp&&this.avatar){
|
if (this.hypotheticalpfp && this.avatar) {
|
||||||
return this.avatar;
|
return this.avatar;
|
||||||
}
|
}
|
||||||
if(this.avatar !== undefined&&this.avatar!==null){
|
if (this.avatar !== undefined && this.avatar !== null) {
|
||||||
return`${this.info.cdn}/guilds/${this.guild.id}/users/${this.id}/avatars${
|
return `${this.info.cdn}/guilds/${this.guild.id}/users/${this.id}/avatars${
|
||||||
this.avatar
|
this.avatar
|
||||||
}.${this.avatar.startsWith("a_")?"gif":"png"}`;
|
}.${this.avatar.startsWith("a_") ? "gif" : "png"}`;
|
||||||
}
|
}
|
||||||
return this.user.getpfpsrc();
|
return this.user.getpfpsrc();
|
||||||
}
|
}
|
||||||
getBannerUrl():string|undefined{
|
getBannerUrl(): string | undefined {
|
||||||
if(this.hypotheticalbanner&&this.banner){
|
if (this.hypotheticalbanner && this.banner) {
|
||||||
return this.banner;
|
return this.banner;
|
||||||
}
|
}
|
||||||
if(this.banner){
|
if (this.banner) {
|
||||||
return `${this.info.cdn}/banners/${this.guild.id}/${
|
return `${this.info.cdn}/banners/${this.guild.id}/${
|
||||||
this.banner
|
this.banner
|
||||||
}.${this.banner.startsWith("a_")?"gif":"png"}`;;
|
}.${this.banner.startsWith("a_") ? "gif" : "png"}`;
|
||||||
}else{
|
} else {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
joined_at!:string;
|
joined_at!: string;
|
||||||
premium_since!:string;
|
premium_since!: string;
|
||||||
deaf!:boolean;
|
deaf!: boolean;
|
||||||
mute!:boolean;
|
mute!: boolean;
|
||||||
pending!:boolean
|
pending!: boolean;
|
||||||
clone(){
|
clone() {
|
||||||
return new Member({
|
return new Member(
|
||||||
id:this.id+"#clone",
|
{
|
||||||
user:this.user.tojson(),
|
id: this.id + "#clone",
|
||||||
guild_id:this.guild.id,
|
user: this.user.tojson(),
|
||||||
guild:{id:this.guild.id},
|
guild_id: this.guild.id,
|
||||||
avatar:this.avatar as (string|undefined),
|
guild: {id: this.guild.id},
|
||||||
banner:this.banner as (string|undefined),
|
avatar: this.avatar as string | undefined,
|
||||||
|
banner: this.banner as string | undefined,
|
||||||
//TODO presence
|
//TODO presence
|
||||||
nick:this.nick,
|
nick: this.nick,
|
||||||
roles:this.roles.map(_=>_.id),
|
roles: this.roles.map((_) => _.id),
|
||||||
joined_at:this.joined_at,
|
joined_at: this.joined_at,
|
||||||
premium_since:this.premium_since,
|
premium_since: this.premium_since,
|
||||||
deaf:this.deaf,
|
deaf: this.deaf,
|
||||||
mute:this.mute,
|
mute: this.mute,
|
||||||
pending:this.pending
|
pending: this.pending,
|
||||||
|
},
|
||||||
},this.owner)
|
this.owner,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
pronouns?:string;
|
pronouns?: string;
|
||||||
bio?:string;
|
bio?: string;
|
||||||
hypotheticalpfp=false;
|
hypotheticalpfp = false;
|
||||||
hypotheticalbanner=false;
|
hypotheticalbanner = false;
|
||||||
accent_color?:number;
|
accent_color?: number;
|
||||||
get headers(){
|
get headers() {
|
||||||
return this.owner.headers;
|
return this.owner.headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
updatepfp(file: Blob): void{
|
updatepfp(file: Blob): void {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
reader.onload = ()=>{
|
reader.onload = () => {
|
||||||
fetch(this.info.api + `/guilds/${this.guild.id}/members/${this.id}/`, {
|
fetch(this.info.api + `/guilds/${this.guild.id}/members/${this.id}/`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
headers: this.headers,
|
headers: this.headers,
|
||||||
|
@ -129,11 +131,11 @@ class Member extends SnowFlake{
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
updatebanner(file: Blob | null): void{
|
updatebanner(file: Blob | null): void {
|
||||||
if(file){
|
if (file) {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
reader.onload = ()=>{
|
reader.onload = () => {
|
||||||
fetch(this.info.api + `/guilds/${this.guild.id}/profile/${this.id}`, {
|
fetch(this.info.api + `/guilds/${this.guild.id}/profile/${this.id}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
headers: this.headers,
|
headers: this.headers,
|
||||||
|
@ -142,7 +144,7 @@ class Member extends SnowFlake{
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}else{
|
} else {
|
||||||
fetch(this.info.api + `/guilds/${this.guild.id}/profile/${this.id}`, {
|
fetch(this.info.api + `/guilds/${this.guild.id}/profile/${this.id}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
headers: this.headers,
|
headers: this.headers,
|
||||||
|
@ -153,11 +155,7 @@ class Member extends SnowFlake{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateProfile(json: {
|
updateProfile(json: {bio?: string | null; pronouns?: string | null; nick?: string | null}) {
|
||||||
bio?: string|null;
|
|
||||||
pronouns?: string|null;
|
|
||||||
nick?:string|null;
|
|
||||||
}){
|
|
||||||
console.log(JSON.stringify(json));
|
console.log(JSON.stringify(json));
|
||||||
/*
|
/*
|
||||||
if(json.bio===""){
|
if(json.bio===""){
|
||||||
|
@ -176,24 +174,26 @@ class Member extends SnowFlake{
|
||||||
body: JSON.stringify(json),
|
body: JSON.stringify(json),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
showEditProfile(){
|
showEditProfile() {
|
||||||
const settings=new Settings("");
|
const settings = new Settings("");
|
||||||
this.editProfile(settings.addButton(I18n.getTranslation("user.editServerProfile"),{ltr:true}));
|
this.editProfile(
|
||||||
|
settings.addButton(I18n.getTranslation("user.editServerProfile"), {ltr: true}),
|
||||||
|
);
|
||||||
settings.show();
|
settings.show();
|
||||||
}
|
}
|
||||||
editProfile(options:Options){
|
editProfile(options: Options) {
|
||||||
if(this.hasPermission("CHANGE_NICKNAME")){
|
if (this.hasPermission("CHANGE_NICKNAME")) {
|
||||||
const hypotheticalProfile = document.createElement("div");
|
const hypotheticalProfile = document.createElement("div");
|
||||||
let file: undefined | File | null;
|
let file: undefined | File | null;
|
||||||
let newpronouns: string | undefined;
|
let newpronouns: string | undefined;
|
||||||
let newbio: string | undefined;
|
let newbio: string | undefined;
|
||||||
let nick:string|undefined;
|
let nick: string | undefined;
|
||||||
const hypomember = this.clone();
|
const hypomember = this.clone();
|
||||||
|
|
||||||
let color: string;
|
let color: string;
|
||||||
async function regen(){
|
async function regen() {
|
||||||
hypotheticalProfile.textContent = "";
|
hypotheticalProfile.textContent = "";
|
||||||
const hypoprofile = await hypomember.user.buildprofile(-1, -1,hypomember);
|
const hypoprofile = await hypomember.user.buildprofile(-1, -1, hypomember);
|
||||||
|
|
||||||
hypotheticalProfile.appendChild(hypoprofile);
|
hypotheticalProfile.appendChild(hypoprofile);
|
||||||
}
|
}
|
||||||
|
@ -202,33 +202,33 @@ class Member extends SnowFlake{
|
||||||
const settingsRight = options.addOptions("");
|
const settingsRight = options.addOptions("");
|
||||||
settingsRight.addHTMLArea(hypotheticalProfile);
|
settingsRight.addHTMLArea(hypotheticalProfile);
|
||||||
|
|
||||||
const nicky=settingsLeft.addTextInput(I18n.getTranslation("member.nick:"),()=>{},{
|
const nicky = settingsLeft.addTextInput(I18n.getTranslation("member.nick:"), () => {}, {
|
||||||
initText:this.nick||""
|
initText: this.nick || "",
|
||||||
});
|
});
|
||||||
nicky.watchForChange(_=>{
|
nicky.watchForChange((_) => {
|
||||||
hypomember.nick=_;
|
hypomember.nick = _;
|
||||||
nick=_;
|
nick = _;
|
||||||
regen();
|
regen();
|
||||||
})
|
});
|
||||||
|
|
||||||
const finput = settingsLeft.addFileInput(
|
const finput = settingsLeft.addFileInput(
|
||||||
I18n.getTranslation("uploadPfp"),
|
I18n.getTranslation("uploadPfp"),
|
||||||
_=>{
|
(_) => {
|
||||||
if(file){
|
if (file) {
|
||||||
this.updatepfp(file);
|
this.updatepfp(file);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ clear: true }
|
{clear: true},
|
||||||
);
|
);
|
||||||
finput.watchForChange(_=>{
|
finput.watchForChange((_) => {
|
||||||
if(!_){
|
if (!_) {
|
||||||
file = null;
|
file = null;
|
||||||
hypomember.avatar = undefined;
|
hypomember.avatar = undefined;
|
||||||
hypomember.hypotheticalpfp = true;
|
hypomember.hypotheticalpfp = true;
|
||||||
regen();
|
regen();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(_.length){
|
if (_.length) {
|
||||||
file = _[0];
|
file = _[0];
|
||||||
const blob = URL.createObjectURL(file);
|
const blob = URL.createObjectURL(file);
|
||||||
hypomember.avatar = blob;
|
hypomember.avatar = blob;
|
||||||
|
@ -239,22 +239,22 @@ class Member extends SnowFlake{
|
||||||
let bfile: undefined | File | null;
|
let bfile: undefined | File | null;
|
||||||
const binput = settingsLeft.addFileInput(
|
const binput = settingsLeft.addFileInput(
|
||||||
I18n.getTranslation("uploadBanner"),
|
I18n.getTranslation("uploadBanner"),
|
||||||
_=>{
|
(_) => {
|
||||||
if(bfile !== undefined){
|
if (bfile !== undefined) {
|
||||||
this.updatebanner(bfile);
|
this.updatebanner(bfile);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ clear: true }
|
{clear: true},
|
||||||
);
|
);
|
||||||
binput.watchForChange(_=>{
|
binput.watchForChange((_) => {
|
||||||
if(!_){
|
if (!_) {
|
||||||
bfile = null;
|
bfile = null;
|
||||||
hypomember.banner = undefined;
|
hypomember.banner = undefined;
|
||||||
hypomember.hypotheticalbanner = true;
|
hypomember.hypotheticalbanner = true;
|
||||||
regen();
|
regen();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(_.length){
|
if (_.length) {
|
||||||
bfile = _[0];
|
bfile = _[0];
|
||||||
const blob = URL.createObjectURL(bfile);
|
const blob = URL.createObjectURL(bfile);
|
||||||
hypomember.banner = blob;
|
hypomember.banner = blob;
|
||||||
|
@ -265,43 +265,43 @@ class Member extends SnowFlake{
|
||||||
let changed = false;
|
let changed = false;
|
||||||
const pronounbox = settingsLeft.addTextInput(
|
const pronounbox = settingsLeft.addTextInput(
|
||||||
I18n.getTranslation("pronouns"),
|
I18n.getTranslation("pronouns"),
|
||||||
_=>{
|
(_) => {
|
||||||
if(newpronouns!==undefined||newbio!==undefined||changed!==undefined){
|
if (newpronouns !== undefined || newbio !== undefined || changed !== undefined) {
|
||||||
this.updateProfile({
|
this.updateProfile({
|
||||||
pronouns: newpronouns,
|
pronouns: newpronouns,
|
||||||
bio: newbio,
|
bio: newbio,
|
||||||
//accent_color: Number.parseInt("0x" + color.substr(1), 16),
|
//accent_color: Number.parseInt("0x" + color.substr(1), 16),
|
||||||
nick
|
nick,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ initText: this.pronouns }
|
{initText: this.pronouns},
|
||||||
);
|
);
|
||||||
pronounbox.watchForChange(_=>{
|
pronounbox.watchForChange((_) => {
|
||||||
hypomember.pronouns = _;
|
hypomember.pronouns = _;
|
||||||
newpronouns = _;
|
newpronouns = _;
|
||||||
regen();
|
regen();
|
||||||
});
|
});
|
||||||
const bioBox = settingsLeft.addMDInput(I18n.getTranslation("bio"), _=>{}, {
|
const bioBox = settingsLeft.addMDInput(I18n.getTranslation("bio"), (_) => {}, {
|
||||||
initText: this.bio,
|
initText: this.bio,
|
||||||
});
|
});
|
||||||
bioBox.watchForChange(_=>{
|
bioBox.watchForChange((_) => {
|
||||||
newbio = _;
|
newbio = _;
|
||||||
hypomember.bio = _;
|
hypomember.bio = _;
|
||||||
regen();
|
regen();
|
||||||
});
|
});
|
||||||
return;//Returns early to stop errors
|
return; //Returns early to stop errors
|
||||||
if(this.accent_color){
|
if (this.accent_color) {
|
||||||
color = "#" + this.accent_color.toString(16);
|
color = "#" + this.accent_color.toString(16);
|
||||||
}else{
|
} else {
|
||||||
color = "transparent";
|
color = "transparent";
|
||||||
}
|
}
|
||||||
const colorPicker = settingsLeft.addColorInput(
|
const colorPicker = settingsLeft.addColorInput(
|
||||||
I18n.getTranslation("profileColor"),
|
I18n.getTranslation("profileColor"),
|
||||||
_=>{},
|
(_) => {},
|
||||||
{ initColor: color }
|
{initColor: color},
|
||||||
);
|
);
|
||||||
colorPicker.watchForChange(_=>{
|
colorPicker.watchForChange((_) => {
|
||||||
console.log();
|
console.log();
|
||||||
color = _;
|
color = _;
|
||||||
hypomember.accent_color = Number.parseInt("0x" + _.substr(1), 16);
|
hypomember.accent_color = Number.parseInt("0x" + _.substr(1), 16);
|
||||||
|
@ -310,116 +310,116 @@ class Member extends SnowFlake{
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
update(memberjson: memberjson){
|
update(memberjson: memberjson) {
|
||||||
if(memberjson.roles){
|
if (memberjson.roles) {
|
||||||
this.roles=[];
|
this.roles = [];
|
||||||
}
|
}
|
||||||
for(const key of Object.keys(memberjson)){
|
for (const key of Object.keys(memberjson)) {
|
||||||
if(key === "guild" || key === "owner" || key === "user"){
|
if (key === "guild" || key === "owner" || key === "user") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(key === "roles"){
|
if (key === "roles") {
|
||||||
for(const strrole of memberjson.roles){
|
for (const strrole of memberjson.roles) {
|
||||||
const role = this.guild.roleids.get(strrole);
|
const role = this.guild.roleids.get(strrole);
|
||||||
if(!role)continue;
|
if (!role) continue;
|
||||||
this.roles.push(role);
|
this.roles.push(role);
|
||||||
}
|
}
|
||||||
if(!this.user.bot){
|
if (!this.user.bot) {
|
||||||
const everyone=this.guild.roleids.get(this.guild.id);
|
const everyone = this.guild.roleids.get(this.guild.id);
|
||||||
if(everyone&&(this.roles.indexOf(everyone)===-1)){
|
if (everyone && this.roles.indexOf(everyone) === -1) {
|
||||||
this.roles.push(everyone)
|
this.roles.push(everyone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if(key === "presence"){
|
if (key === "presence") {
|
||||||
this.getPresence(memberjson.presence);
|
this.getPresence(memberjson.presence);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
(this as any)[key] = (memberjson as any)[key];
|
(this as any)[key] = (memberjson as any)[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.roles.sort((a, b)=>{
|
this.roles.sort((a, b) => {
|
||||||
return this.guild.roles.indexOf(a) - this.guild.roles.indexOf(b);
|
return this.guild.roles.indexOf(a) - this.guild.roles.indexOf(b);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
get guild(){
|
get guild() {
|
||||||
return this.owner;
|
return this.owner;
|
||||||
}
|
}
|
||||||
get localuser(){
|
get localuser() {
|
||||||
return this.guild.localuser;
|
return this.guild.localuser;
|
||||||
}
|
}
|
||||||
get info(){
|
get info() {
|
||||||
return this.owner.info;
|
return this.owner.info;
|
||||||
}
|
}
|
||||||
static async new(
|
static async new(memberjson: memberjson, owner: Guild): Promise<Member | undefined> {
|
||||||
memberjson: memberjson,
|
|
||||||
owner: Guild
|
|
||||||
): Promise<Member | undefined>{
|
|
||||||
let user: User;
|
let user: User;
|
||||||
if(owner.localuser.userMap.has(memberjson.id)){
|
if (owner.localuser.userMap.has(memberjson.id)) {
|
||||||
if(memberjson.user){
|
if (memberjson.user) {
|
||||||
new User(memberjson.user, owner.localuser);
|
new User(memberjson.user, owner.localuser);
|
||||||
}
|
}
|
||||||
user = owner.localuser.userMap.get(memberjson.id) as User;
|
user = owner.localuser.userMap.get(memberjson.id) as User;
|
||||||
}else if(memberjson.user){
|
} else if (memberjson.user) {
|
||||||
user = new User(memberjson.user, owner.localuser);
|
user = new User(memberjson.user, owner.localuser);
|
||||||
}else{
|
} else {
|
||||||
throw new Error("missing user object of this member");
|
throw new Error("missing user object of this member");
|
||||||
}
|
}
|
||||||
if(user.members.has(owner)){
|
if (user.members.has(owner)) {
|
||||||
let memb = user.members.get(owner);
|
let memb = user.members.get(owner);
|
||||||
if(memb === undefined){
|
if (memb === undefined) {
|
||||||
memb = new Member(memberjson, owner);
|
memb = new Member(memberjson, owner);
|
||||||
user.members.set(owner, memb);
|
user.members.set(owner, memb);
|
||||||
owner.members.add(memb);
|
owner.members.add(memb);
|
||||||
return memb;
|
return memb;
|
||||||
}else if(memb instanceof Promise){
|
} else if (memb instanceof Promise) {
|
||||||
const member=await memb; //I should do something else, though for now this is "good enough";
|
const member = await memb; //I should do something else, though for now this is "good enough";
|
||||||
if(member){
|
if (member) {
|
||||||
member.update(memberjson);
|
member.update(memberjson);
|
||||||
}
|
}
|
||||||
return member;
|
return member;
|
||||||
}else{
|
} else {
|
||||||
if(memberjson.presence){
|
if (memberjson.presence) {
|
||||||
memb.getPresence(memberjson.presence);
|
memb.getPresence(memberjson.presence);
|
||||||
}
|
}
|
||||||
memb.update(memberjson);
|
memb.update(memberjson);
|
||||||
return memb;
|
return memb;
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
const memb = new Member(memberjson, owner);
|
const memb = new Member(memberjson, owner);
|
||||||
user.members.set(owner, memb);
|
user.members.set(owner, memb);
|
||||||
owner.members.add(memb);
|
owner.members.add(memb);
|
||||||
return memb;
|
return memb;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compare(str:string){
|
compare(str: string) {
|
||||||
function similar(str2:string|null|undefined){
|
function similar(str2: string | null | undefined) {
|
||||||
if(!str2) return 0;
|
if (!str2) return 0;
|
||||||
const strl=Math.max(str.length,1)
|
const strl = Math.max(str.length, 1);
|
||||||
if(str2.includes(str)){
|
if (str2.includes(str)) {
|
||||||
return strl/str2.length;
|
return strl / str2.length;
|
||||||
}else if(str2.toLowerCase().includes(str.toLowerCase())){
|
} else if (str2.toLowerCase().includes(str.toLowerCase())) {
|
||||||
return strl/str2.length/1.2;
|
return strl / str2.length / 1.2;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return Math.max(similar(this.user.name),similar(this.user.nickname),similar(this.nick),similar(this.user.username),similar(this.id)/1.5);
|
return Math.max(
|
||||||
|
similar(this.user.name),
|
||||||
|
similar(this.user.nickname),
|
||||||
|
similar(this.nick),
|
||||||
|
similar(this.user.username),
|
||||||
|
similar(this.id) / 1.5,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
static async resolveMember(
|
static async resolveMember(user: User, guild: Guild): Promise<Member | undefined> {
|
||||||
user: User,
|
|
||||||
guild: Guild
|
|
||||||
): Promise<Member | undefined>{
|
|
||||||
const maybe = user.members.get(guild);
|
const maybe = user.members.get(guild);
|
||||||
if(!user.members.has(guild)){
|
if (!user.members.has(guild)) {
|
||||||
const membpromise = guild.localuser.resolvemember(user.id, guild.id);
|
const membpromise = guild.localuser.resolvemember(user.id, guild.id);
|
||||||
const promise = new Promise<Member | undefined>(async res=>{
|
const promise = new Promise<Member | undefined>(async (res) => {
|
||||||
const membjson = await membpromise;
|
const membjson = await membpromise;
|
||||||
if(membjson === undefined){
|
if (membjson === undefined) {
|
||||||
return res(undefined);
|
return res(undefined);
|
||||||
}else{
|
} else {
|
||||||
const member = new Member(membjson, guild);
|
const member = new Member(membjson, guild);
|
||||||
const map = guild.localuser.presences;
|
const map = guild.localuser.presences;
|
||||||
member.getPresence(map.get(member.id));
|
member.getPresence(map.get(member.id));
|
||||||
|
@ -429,62 +429,62 @@ class Member extends SnowFlake{
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
user.members.set(guild, promise);
|
user.members.set(guild, promise);
|
||||||
const member=await promise;
|
const member = await promise;
|
||||||
if(member){
|
if (member) {
|
||||||
guild.members.add(member);
|
guild.members.add(member);
|
||||||
}
|
}
|
||||||
return member;
|
return member;
|
||||||
}
|
}
|
||||||
if(maybe instanceof Promise){
|
if (maybe instanceof Promise) {
|
||||||
return await maybe;
|
return await maybe;
|
||||||
}else{
|
} else {
|
||||||
return maybe;
|
return maybe;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public getPresence(presence: presencejson | undefined){
|
public getPresence(presence: presencejson | undefined) {
|
||||||
this.user.getPresence(presence);
|
this.user.getPresence(presence);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @todo
|
* @todo
|
||||||
*/
|
*/
|
||||||
highInfo(){
|
highInfo() {
|
||||||
fetch(
|
fetch(
|
||||||
this.info.api +
|
this.info.api +
|
||||||
"/users/" +
|
"/users/" +
|
||||||
this.id +
|
this.id +
|
||||||
"/profile?with_mutual_guilds=true&with_mutual_friends_count=true&guild_id=" +
|
"/profile?with_mutual_guilds=true&with_mutual_friends_count=true&guild_id=" +
|
||||||
this.guild.id,
|
this.guild.id,
|
||||||
{ headers: this.guild.headers }
|
{headers: this.guild.headers},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
hasRole(ID: string){
|
hasRole(ID: string) {
|
||||||
for(const thing of this.roles){
|
for (const thing of this.roles) {
|
||||||
if(thing.id === ID){
|
if (thing.id === ID) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
getColor(){
|
getColor() {
|
||||||
for(const thing of this.roles){
|
for (const thing of this.roles) {
|
||||||
const color = thing.getColor();
|
const color = thing.getColor();
|
||||||
if(color){
|
if (color) {
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return"";
|
return "";
|
||||||
}
|
}
|
||||||
isAdmin(){
|
isAdmin() {
|
||||||
for(const role of this.roles){
|
for (const role of this.roles) {
|
||||||
if(role.permissions.getPermission("ADMINISTRATOR")){
|
if (role.permissions.getPermission("ADMINISTRATOR")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.guild.properties.owner_id === this.user.id;
|
return this.guild.properties.owner_id === this.user.id;
|
||||||
}
|
}
|
||||||
bind(html: HTMLElement){
|
bind(html: HTMLElement) {
|
||||||
if(html.tagName === "SPAN"){
|
if (html.tagName === "SPAN") {
|
||||||
if(!this){
|
if (!this) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
@ -497,23 +497,23 @@ class Member extends SnowFlake{
|
||||||
|
|
||||||
//this.profileclick(html);
|
//this.profileclick(html);
|
||||||
}
|
}
|
||||||
profileclick(/* html: HTMLElement */){
|
profileclick(/* html: HTMLElement */) {
|
||||||
//to be implemented
|
//to be implemented
|
||||||
}
|
}
|
||||||
get name(){
|
get name() {
|
||||||
return this.nick || this.user.username;
|
return this.nick || this.user.username;
|
||||||
}
|
}
|
||||||
kick(){
|
kick() {
|
||||||
const menu = new Dialog("");
|
const menu = new Dialog("");
|
||||||
const form=menu.options.addForm("",((e:any)=>{
|
const form = menu.options.addForm("", (e: any) => {
|
||||||
this.kickAPI(e.reason);
|
this.kickAPI(e.reason);
|
||||||
menu.hide();
|
menu.hide();
|
||||||
}));
|
});
|
||||||
form.addTitle(I18n.getTranslation("member.kick",this.name,this.guild.properties.name));
|
form.addTitle(I18n.getTranslation("member.kick", this.name, this.guild.properties.name));
|
||||||
form.addTextInput(I18n.getTranslation("member.reason:"),"reason");
|
form.addTextInput(I18n.getTranslation("member.reason:"), "reason");
|
||||||
menu.show();
|
menu.show();
|
||||||
}
|
}
|
||||||
kickAPI(reason: string){
|
kickAPI(reason: string) {
|
||||||
const headers = structuredClone(this.guild.headers);
|
const headers = structuredClone(this.guild.headers);
|
||||||
(headers as any)["x-audit-log-reason"] = reason;
|
(headers as any)["x-audit-log-reason"] = reason;
|
||||||
fetch(`${this.info.api}/guilds/${this.guild.id}/members/${this.id}`, {
|
fetch(`${this.info.api}/guilds/${this.guild.id}/members/${this.id}`, {
|
||||||
|
@ -521,35 +521,35 @@ class Member extends SnowFlake{
|
||||||
headers,
|
headers,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ban(){
|
ban() {
|
||||||
const menu = new Dialog("");
|
const menu = new Dialog("");
|
||||||
const form=menu.options.addForm("",((e:any)=>{
|
const form = menu.options.addForm("", (e: any) => {
|
||||||
this.banAPI(e.reason);
|
this.banAPI(e.reason);
|
||||||
menu.hide();
|
menu.hide();
|
||||||
}));
|
});
|
||||||
form.addTitle(I18n.getTranslation("member.ban",this.name,this.guild.properties.name));
|
form.addTitle(I18n.getTranslation("member.ban", this.name, this.guild.properties.name));
|
||||||
form.addTextInput(I18n.getTranslation("member.reason:"),"reason");
|
form.addTextInput(I18n.getTranslation("member.reason:"), "reason");
|
||||||
menu.show();
|
menu.show();
|
||||||
}
|
}
|
||||||
addRole(role:Role){
|
addRole(role: Role) {
|
||||||
const roles=this.roles.map(_=>_.id)
|
const roles = this.roles.map((_) => _.id);
|
||||||
roles.push(role.id);
|
roles.push(role.id);
|
||||||
fetch(this.info.api+"/guilds/"+this.guild.id+"/members/"+this.id,{
|
fetch(this.info.api + "/guilds/" + this.guild.id + "/members/" + this.id, {
|
||||||
method:"PATCH",
|
method: "PATCH",
|
||||||
headers:this.guild.headers,
|
headers: this.guild.headers,
|
||||||
body:JSON.stringify({roles})
|
body: JSON.stringify({roles}),
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
removeRole(role:Role){
|
removeRole(role: Role) {
|
||||||
let roles=this.roles.map(_=>_.id)
|
let roles = this.roles.map((_) => _.id);
|
||||||
roles=roles.filter(_=>_!==role.id);
|
roles = roles.filter((_) => _ !== role.id);
|
||||||
fetch(this.info.api+"/guilds/"+this.guild.id+"/members/"+this.id,{
|
fetch(this.info.api + "/guilds/" + this.guild.id + "/members/" + this.id, {
|
||||||
method:"PATCH",
|
method: "PATCH",
|
||||||
headers:this.guild.headers,
|
headers: this.guild.headers,
|
||||||
body:JSON.stringify({roles})
|
body: JSON.stringify({roles}),
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
banAPI(reason: string){
|
banAPI(reason: string) {
|
||||||
const headers = structuredClone(this.guild.headers);
|
const headers = structuredClone(this.guild.headers);
|
||||||
(headers as any)["x-audit-log-reason"] = reason;
|
(headers as any)["x-audit-log-reason"] = reason;
|
||||||
fetch(`${this.info.api}/guilds/${this.guild.id}/bans/${this.id}`, {
|
fetch(`${this.info.api}/guilds/${this.guild.id}/bans/${this.id}`, {
|
||||||
|
@ -557,16 +557,16 @@ class Member extends SnowFlake{
|
||||||
headers,
|
headers,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
hasPermission(name: string): boolean{
|
hasPermission(name: string): boolean {
|
||||||
if(this.isAdmin()){
|
if (this.isAdmin()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for(const thing of this.roles){
|
for (const thing of this.roles) {
|
||||||
if(thing.permissions.getPermission(name)){
|
if (thing.permissions.getPermission(name)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export{ Member };
|
export {Member};
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,157 +1,157 @@
|
||||||
import { I18n } from "../i18n.js";
|
import {I18n} from "../i18n.js";
|
||||||
import{ getapiurls }from"../utils/utils.js";
|
import {getapiurls} from "../utils/utils.js";
|
||||||
import { getBulkUsers, Specialuser } from "../utils/utils.js";
|
import {getBulkUsers, Specialuser} from "../utils/utils.js";
|
||||||
import { Permissions } from "../permissions.js";
|
import {Permissions} from "../permissions.js";
|
||||||
type botjsonfetch={
|
type botjsonfetch = {
|
||||||
guilds:{
|
guilds: {
|
||||||
id: string,
|
id: string;
|
||||||
name: string,
|
name: string;
|
||||||
icon: string,
|
icon: string;
|
||||||
mfa_level: number,
|
mfa_level: number;
|
||||||
permissions: string
|
permissions: string;
|
||||||
}[],
|
}[];
|
||||||
"user": {
|
user: {
|
||||||
id: string,
|
id: string;
|
||||||
username: string,
|
username: string;
|
||||||
avatar: string,
|
avatar: string;
|
||||||
avatar_decoration?: string,
|
avatar_decoration?: string;
|
||||||
discriminator: string,
|
discriminator: string;
|
||||||
public_flags: number
|
public_flags: number;
|
||||||
},
|
};
|
||||||
application: {
|
application: {
|
||||||
id: string,
|
id: string;
|
||||||
name: string,
|
name: string;
|
||||||
icon: string|null,
|
icon: string | null;
|
||||||
description: string,
|
description: string;
|
||||||
summary: string,
|
summary: string;
|
||||||
type: null,//not sure what this means :P
|
type: null; //not sure what this means :P
|
||||||
hook: boolean,
|
hook: boolean;
|
||||||
guild_id: null|string,
|
guild_id: null | string;
|
||||||
bot_public: boolean,
|
bot_public: boolean;
|
||||||
bot_require_code_grant: boolean,
|
bot_require_code_grant: boolean;
|
||||||
verify_key: "IMPLEMENTME",//no clue what this is meant to be :P
|
verify_key: "IMPLEMENTME"; //no clue what this is meant to be :P
|
||||||
flags: number
|
flags: number;
|
||||||
},
|
};
|
||||||
bot: {
|
bot: {
|
||||||
id: string,
|
id: string;
|
||||||
username: string,
|
username: string;
|
||||||
avatar: string|null,
|
avatar: string | null;
|
||||||
avatar_decoration: null|string,
|
avatar_decoration: null | string;
|
||||||
discriminator: string,
|
discriminator: string;
|
||||||
public_flags: number,
|
public_flags: number;
|
||||||
bot: boolean,
|
bot: boolean;
|
||||||
approximated_guild_count: number
|
approximated_guild_count: number;
|
||||||
},
|
};
|
||||||
authorized: boolean
|
authorized: boolean;
|
||||||
}
|
};
|
||||||
(async ()=>{
|
(async () => {
|
||||||
const users = getBulkUsers();
|
const users = getBulkUsers();
|
||||||
const params=new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
const well = params.get("instance");
|
const well = params.get("instance");
|
||||||
const permstr=params.get("permissions");
|
const permstr = params.get("permissions");
|
||||||
const joinable: Specialuser[] = [];
|
const joinable: Specialuser[] = [];
|
||||||
|
|
||||||
for(const key in users.users){
|
for (const key in users.users) {
|
||||||
if(Object.prototype.hasOwnProperty.call(users.users, key)){
|
if (Object.prototype.hasOwnProperty.call(users.users, key)) {
|
||||||
const user: Specialuser = users.users[key];
|
const user: Specialuser = users.users[key];
|
||||||
if(well && user.serverurls.wellknown.includes(well)){
|
if (well && user.serverurls.wellknown.includes(well)) {
|
||||||
joinable.push(user);
|
joinable.push(user);
|
||||||
}
|
}
|
||||||
console.log(user);
|
console.log(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let urls: { api: string; cdn: string } | undefined;
|
let urls: {api: string; cdn: string} | undefined;
|
||||||
|
|
||||||
if(!joinable.length && well){
|
if (!joinable.length && well) {
|
||||||
const out = await getapiurls(well);
|
const out = await getapiurls(well);
|
||||||
if(out){
|
if (out) {
|
||||||
urls = out;
|
urls = out;
|
||||||
for(const key in users.users){
|
for (const key in users.users) {
|
||||||
if(Object.prototype.hasOwnProperty.call(users.users, key)){
|
if (Object.prototype.hasOwnProperty.call(users.users, key)) {
|
||||||
const user: Specialuser = users.users[key];
|
const user: Specialuser = users.users[key];
|
||||||
if(user.serverurls.api.includes(out.api)){
|
if (user.serverurls.api.includes(out.api)) {
|
||||||
joinable.push(user);
|
joinable.push(user);
|
||||||
}
|
}
|
||||||
console.log(user);
|
console.log(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
throw new Error(
|
throw new Error("Someone needs to handle the case where the servers don't exist");
|
||||||
"Someone needs to handle the case where the servers don't exist"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
urls = joinable[0].serverurls;
|
urls = joinable[0].serverurls;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!joinable.length){
|
if (!joinable.length) {
|
||||||
document.getElementById("AcceptInvite")!.textContent = "Create an account to invite the bot";
|
document.getElementById("AcceptInvite")!.textContent = "Create an account to invite the bot";
|
||||||
}
|
}
|
||||||
await I18n.done;
|
await I18n.done;
|
||||||
function showGuilds(user:Specialuser){
|
function showGuilds(user: Specialuser) {
|
||||||
if(!urls) return;
|
if (!urls) return;
|
||||||
fetch(urls.api+"/oauth2/authorize/"+window.location.search,{
|
fetch(urls.api + "/oauth2/authorize/" + window.location.search, {
|
||||||
headers:{
|
headers: {
|
||||||
Authorization:user.token
|
Authorization: user.token,
|
||||||
}
|
},
|
||||||
}).then(_=>_.json()).then((json:botjsonfetch)=>{
|
})
|
||||||
const guilds:botjsonfetch["guilds"]=[];
|
.then((_) => _.json())
|
||||||
for(const guild of json.guilds){
|
.then((json: botjsonfetch) => {
|
||||||
const permisions=new Permissions(guild.permissions)
|
const guilds: botjsonfetch["guilds"] = [];
|
||||||
if(permisions.hasPermission("MANAGE_GUILD")){
|
for (const guild of json.guilds) {
|
||||||
|
const permisions = new Permissions(guild.permissions);
|
||||||
|
if (permisions.hasPermission("MANAGE_GUILD")) {
|
||||||
guilds.push(guild);
|
guilds.push(guild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const dialog=document.createElement("dialog");
|
const dialog = document.createElement("dialog");
|
||||||
dialog.classList.add("flexttb","accountSwitcher");
|
dialog.classList.add("flexttb", "accountSwitcher");
|
||||||
const h1=document.createElement("h1");
|
const h1 = document.createElement("h1");
|
||||||
dialog.append(h1);
|
dialog.append(h1);
|
||||||
h1.textContent="Invite to server:";
|
h1.textContent = "Invite to server:";
|
||||||
const select=document.createElement("select");
|
const select = document.createElement("select");
|
||||||
const selectSpan=document.createElement("span");
|
const selectSpan = document.createElement("span");
|
||||||
selectSpan.classList.add("selectspan");
|
selectSpan.classList.add("selectspan");
|
||||||
const selectArrow = document.createElement("span");
|
const selectArrow = document.createElement("span");
|
||||||
selectArrow.classList.add("svgicon","svg-category","selectarrow");
|
selectArrow.classList.add("svgicon", "svg-category", "selectarrow");
|
||||||
for(const guild of guilds){
|
for (const guild of guilds) {
|
||||||
const option=document.createElement("option");
|
const option = document.createElement("option");
|
||||||
option.textContent=guild.name;
|
option.textContent = guild.name;
|
||||||
option.value=guild.id;
|
option.value = guild.id;
|
||||||
select.append(option);
|
select.append(option);
|
||||||
}
|
}
|
||||||
selectSpan.append(select);
|
selectSpan.append(select);
|
||||||
selectSpan.append(selectArrow);
|
selectSpan.append(selectArrow);
|
||||||
dialog.append(selectSpan);
|
dialog.append(selectSpan);
|
||||||
const button=document.createElement("button");
|
const button = document.createElement("button");
|
||||||
button.textContent="Invite";
|
button.textContent = "Invite";
|
||||||
dialog.append(button);
|
dialog.append(button);
|
||||||
button.onclick=()=>{
|
button.onclick = () => {
|
||||||
const id=select.value;
|
const id = select.value;
|
||||||
const params2=new URLSearchParams("");
|
const params2 = new URLSearchParams("");
|
||||||
params2.set("client_id",params.get("client_id") as string)
|
params2.set("client_id", params.get("client_id") as string);
|
||||||
fetch(urls.api+"/oauth2/authorize?"+params2.toString(),{
|
fetch(urls.api + "/oauth2/authorize?" + params2.toString(), {
|
||||||
method:"POST",
|
method: "POST",
|
||||||
body:JSON.stringify({
|
body: JSON.stringify({
|
||||||
authorize:true,
|
authorize: true,
|
||||||
guild_id:id,
|
guild_id: id,
|
||||||
permissions:permstr
|
permissions: permstr,
|
||||||
}),
|
}),
|
||||||
headers:{
|
headers: {
|
||||||
"Content-type": "application/json; charset=UTF-8",
|
"Content-type": "application/json; charset=UTF-8",
|
||||||
Authorization:user.token,
|
Authorization: user.token,
|
||||||
}
|
},
|
||||||
}).then(req=>{
|
}).then((req) => {
|
||||||
if(req.ok){
|
if (req.ok) {
|
||||||
alert("Bot added successfully");
|
alert("Bot added successfully");
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
document.body.append(dialog);
|
document.body.append(dialog);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
function showAccounts(): void{
|
function showAccounts(): void {
|
||||||
const table = document.createElement("dialog");
|
const table = document.createElement("dialog");
|
||||||
for(const user of joinable){
|
for (const user of joinable) {
|
||||||
console.log(user.pfpsrc);
|
console.log(user.pfpsrc);
|
||||||
|
|
||||||
const userinfo = document.createElement("div");
|
const userinfo = document.createElement("div");
|
||||||
|
@ -168,16 +168,14 @@ type botjsonfetch={
|
||||||
userDiv.append(document.createElement("br"));
|
userDiv.append(document.createElement("br"));
|
||||||
|
|
||||||
const span = document.createElement("span");
|
const span = document.createElement("span");
|
||||||
span.textContent = user.serverurls.wellknown
|
span.textContent = user.serverurls.wellknown.replace("https://", "").replace("http://", "");
|
||||||
.replace("https://", "")
|
|
||||||
.replace("http://", "");
|
|
||||||
span.classList.add("serverURL");
|
span.classList.add("serverURL");
|
||||||
userDiv.append(span);
|
userDiv.append(span);
|
||||||
|
|
||||||
userinfo.append(userDiv);
|
userinfo.append(userDiv);
|
||||||
table.append(userinfo);
|
table.append(userinfo);
|
||||||
|
|
||||||
userinfo.addEventListener("click", ()=>{
|
userinfo.addEventListener("click", () => {
|
||||||
table.remove();
|
table.remove();
|
||||||
showGuilds(user);
|
showGuilds(user);
|
||||||
});
|
});
|
||||||
|
@ -186,14 +184,14 @@ type botjsonfetch={
|
||||||
const td = document.createElement("div");
|
const td = document.createElement("div");
|
||||||
td.classList.add("switchtable");
|
td.classList.add("switchtable");
|
||||||
td.textContent = "Login or create an account ⇌";
|
td.textContent = "Login or create an account ⇌";
|
||||||
td.addEventListener("click", ()=>{
|
td.addEventListener("click", () => {
|
||||||
const l = new URLSearchParams("?");
|
const l = new URLSearchParams("?");
|
||||||
l.set("goback", window.location.href);
|
l.set("goback", window.location.href);
|
||||||
l.set("instance", well!);
|
l.set("instance", well!);
|
||||||
window.location.href = "/login?" + l.toString();
|
window.location.href = "/login?" + l.toString();
|
||||||
});
|
});
|
||||||
|
|
||||||
if(!joinable.length){
|
if (!joinable.length) {
|
||||||
const l = new URLSearchParams("?");
|
const l = new URLSearchParams("?");
|
||||||
l.set("goback", window.location.href);
|
l.set("goback", window.location.href);
|
||||||
l.set("instance", well!);
|
l.set("instance", well!);
|
||||||
|
@ -201,54 +199,56 @@ type botjsonfetch={
|
||||||
}
|
}
|
||||||
|
|
||||||
table.append(td);
|
table.append(td);
|
||||||
table.classList.add("flexttb","accountSwitcher");
|
table.classList.add("flexttb", "accountSwitcher");
|
||||||
console.log(table);
|
console.log(table);
|
||||||
document.body.append(table);
|
document.body.append(table);
|
||||||
}
|
}
|
||||||
const user=joinable[0];
|
const user = joinable[0];
|
||||||
if(!user){
|
if (!user) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fetch(urls.api+"/oauth2/authorize/"+window.location.search,{
|
fetch(urls.api + "/oauth2/authorize/" + window.location.search, {
|
||||||
headers:{
|
headers: {
|
||||||
Authorization:user.token
|
Authorization: user.token,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((_) => _.json())
|
||||||
|
.then((json: botjsonfetch) => {
|
||||||
|
const title = document.getElementById("invitename");
|
||||||
|
if (title) {
|
||||||
|
title.textContent = `Invite ${json.bot.username} to your servers`;
|
||||||
}
|
}
|
||||||
}).then(_=>_.json()).then((json:botjsonfetch)=>{
|
const desc = document.getElementById("invitedescription");
|
||||||
const title=document.getElementById("invitename");
|
if (desc) {
|
||||||
if(title){
|
desc.textContent = json.application.description;
|
||||||
title.textContent=`Invite ${json.bot.username} to your servers`
|
|
||||||
}
|
}
|
||||||
const desc=document.getElementById("invitedescription");
|
const pfp = document.getElementById("inviteimg") as HTMLImageElement;
|
||||||
if(desc){
|
if (json.bot.avatar !== null) {
|
||||||
desc.textContent=json.application.description;
|
pfp.src = `${urls.cdn}/avatars/${json.bot.id}/${json.bot.avatar}.png`;
|
||||||
}
|
} else {
|
||||||
const pfp=document.getElementById("inviteimg") as HTMLImageElement;
|
|
||||||
if(json.bot.avatar !== null){
|
|
||||||
pfp.src=`${urls.cdn}/avatars/${json.bot.id}/${json.bot.avatar}.png`;
|
|
||||||
}else{
|
|
||||||
const int = Number((BigInt(json.bot.id) >> 22n) % 6n);
|
const int = Number((BigInt(json.bot.id) >> 22n) % 6n);
|
||||||
pfp.src=`${urls.cdn}/embed/avatars/${int}.png`;
|
pfp.src = `${urls.cdn}/embed/avatars/${int}.png`;
|
||||||
}
|
}
|
||||||
const perms=document.getElementById("permissions") as HTMLDivElement;
|
const perms = document.getElementById("permissions") as HTMLDivElement;
|
||||||
|
|
||||||
if(perms&&permstr){
|
if (perms && permstr) {
|
||||||
perms.children[0].textContent=I18n.getTranslation("htmlPages.idpermissions")
|
perms.children[0].textContent = I18n.getTranslation("htmlPages.idpermissions");
|
||||||
const permisions=new Permissions(permstr)
|
const permisions = new Permissions(permstr);
|
||||||
for(const perm of Permissions.info()){
|
for (const perm of Permissions.info()) {
|
||||||
if(permisions.hasPermission(perm.name,false)){
|
if (permisions.hasPermission(perm.name, false)) {
|
||||||
const div=document.createElement("div");
|
const div = document.createElement("div");
|
||||||
const h2=document.createElement("h2");
|
const h2 = document.createElement("h2");
|
||||||
h2.textContent=perm.readableName;
|
h2.textContent = perm.readableName;
|
||||||
div.append(h2,perm.description);
|
div.append(h2, perm.description);
|
||||||
div.classList.add("flexttb");
|
div.classList.add("flexttb");
|
||||||
perms.append(div);
|
perms.append(div);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
const AcceptInvite=document.getElementById("AcceptInvite");
|
const AcceptInvite = document.getElementById("AcceptInvite");
|
||||||
if(AcceptInvite){
|
if (AcceptInvite) {
|
||||||
AcceptInvite.addEventListener("click", showAccounts);
|
AcceptInvite.addEventListener("click", showAccounts);
|
||||||
AcceptInvite.textContent=I18n.getTranslation("htmlPages.addBot")
|
AcceptInvite.textContent = I18n.getTranslation("htmlPages.addBot");
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -1,21 +1,30 @@
|
||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Jank Client</title>
|
<title>Jank Client</title>
|
||||||
<meta content="Bot Invite" property="og:title">
|
<meta content="Bot Invite" property="og:title" />
|
||||||
<meta name="description" content="Invite this bot to your server!">
|
<meta name="description" content="Invite this bot to your server!" />
|
||||||
<meta content="/logo.webp" property="og:image">
|
<meta content="/logo.webp" property="og:image" />
|
||||||
<meta content="#4b458c" data-react-helmet="true" name="theme-color">
|
<meta content="#4b458c" data-react-helmet="true" name="theme-color" />
|
||||||
<link href="/style.css" rel="stylesheet">
|
<link href="/style.css" rel="stylesheet" />
|
||||||
<link href="/themes.css" rel="stylesheet" id="lightcss">
|
<link href="/themes.css" rel="stylesheet" id="lightcss" />
|
||||||
<style>body.no-theme{background:#16191b;}@media(prefers-color-scheme:light){body.no-theme{background:#9397bd;}}</style>
|
<style>
|
||||||
|
body.no-theme {
|
||||||
|
background: #16191b;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
body.no-theme {
|
||||||
|
background: #9397bd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="no-theme">
|
<body class="no-theme">
|
||||||
<div>
|
<div>
|
||||||
<div id="invitebody">
|
<div id="invitebody">
|
||||||
<img id="inviteimg" class="pfp"/>
|
<img id="inviteimg" class="pfp" />
|
||||||
<h1 id="invitename">Bot Name</h1>
|
<h1 id="invitename">Bot Name</h1>
|
||||||
<p id="invitedescription">Add Bot</p>
|
<p id="invitedescription">Add Bot</p>
|
||||||
<div id="permissions"><h1>This will allow the bot to:</h1></div>
|
<div id="permissions"><h1>This will allow the bot to:</h1></div>
|
||||||
|
|
|
@ -1,40 +1,40 @@
|
||||||
import { I18n } from "./i18n.js";
|
import {I18n} from "./i18n.js";
|
||||||
|
|
||||||
class Permissions{
|
class Permissions {
|
||||||
allow: bigint;
|
allow: bigint;
|
||||||
deny: bigint;
|
deny: bigint;
|
||||||
readonly hasDeny: boolean;
|
readonly hasDeny: boolean;
|
||||||
constructor(allow: string, deny: string = ""){
|
constructor(allow: string, deny: string = "") {
|
||||||
this.hasDeny = Boolean(deny);
|
this.hasDeny = Boolean(deny);
|
||||||
try{
|
try {
|
||||||
this.allow = BigInt(allow);
|
this.allow = BigInt(allow);
|
||||||
this.deny = BigInt(deny);
|
this.deny = BigInt(deny);
|
||||||
}catch{
|
} catch {
|
||||||
this.allow = 0n;
|
this.allow = 0n;
|
||||||
this.deny = 0n;
|
this.deny = 0n;
|
||||||
console.error(
|
console.error(
|
||||||
`Something really stupid happened with a permission with allow being ${allow} and deny being, ${deny}, execution will still happen, but something really stupid happened, please report if you know what caused this.`
|
`Something really stupid happened with a permission with allow being ${allow} and deny being, ${deny}, execution will still happen, but something really stupid happened, please report if you know what caused this.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getPermissionbit(b: number, big: bigint): boolean{
|
getPermissionbit(b: number, big: bigint): boolean {
|
||||||
return Boolean((big >> BigInt(b)) & 1n);
|
return Boolean((big >> BigInt(b)) & 1n);
|
||||||
}
|
}
|
||||||
setPermissionbit(b: number, state: boolean, big: bigint): bigint{
|
setPermissionbit(b: number, state: boolean, big: bigint): bigint {
|
||||||
const bit = 1n << BigInt(b);
|
const bit = 1n << BigInt(b);
|
||||||
return(big & ~bit) | (BigInt(state) << BigInt(b)); //thanks to geotale for this code :3
|
return (big & ~bit) | (BigInt(state) << BigInt(b)); //thanks to geotale for this code :3
|
||||||
}
|
}
|
||||||
//private static info: { name: string; readableName: string; description: string }[];
|
//private static info: { name: string; readableName: string; description: string }[];
|
||||||
static *info():Generator<{ name: string; readableName: string; description: string }>{
|
static *info(): Generator<{name: string; readableName: string; description: string}> {
|
||||||
for(const thing of this.permisions){
|
for (const thing of this.permisions) {
|
||||||
yield {
|
yield {
|
||||||
name:thing,
|
name: thing,
|
||||||
readableName:I18n.getTranslation("permissions.readableNames."+thing),
|
readableName: I18n.getTranslation("permissions.readableNames." + thing),
|
||||||
description:I18n.getTranslation("permissions.descriptions."+thing),
|
description: I18n.getTranslation("permissions.descriptions." + thing),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
static permisions = [
|
||||||
static permisions=[
|
|
||||||
"CREATE_INSTANT_INVITE",
|
"CREATE_INSTANT_INVITE",
|
||||||
"KICK_MEMBERS",
|
"KICK_MEMBERS",
|
||||||
"BAN_MEMBERS",
|
"BAN_MEMBERS",
|
||||||
|
@ -83,57 +83,50 @@ class Permissions{
|
||||||
"USE_EXTERNAL_SOUNDS",
|
"USE_EXTERNAL_SOUNDS",
|
||||||
"SEND_VOICE_MESSAGES",
|
"SEND_VOICE_MESSAGES",
|
||||||
"SEND_POLLS",
|
"SEND_POLLS",
|
||||||
"USE_EXTERNAL_APPS"
|
"USE_EXTERNAL_APPS",
|
||||||
];
|
];
|
||||||
getPermission(name: string): number{
|
getPermission(name: string): number {
|
||||||
if(undefined===Permissions.permisions.indexOf(name)){
|
if (undefined === Permissions.permisions.indexOf(name)) {
|
||||||
console.error(name +" is not found in map",Permissions.permisions);
|
console.error(name + " is not found in map", Permissions.permisions);
|
||||||
}
|
}
|
||||||
if(this.getPermissionbit(Permissions.permisions.indexOf(name), this.allow)){
|
if (this.getPermissionbit(Permissions.permisions.indexOf(name), this.allow)) {
|
||||||
return 1;
|
return 1;
|
||||||
}else if(
|
} else if (this.getPermissionbit(Permissions.permisions.indexOf(name), this.deny)) {
|
||||||
this.getPermissionbit(Permissions.permisions.indexOf(name), this.deny)
|
return -1;
|
||||||
){
|
} else {
|
||||||
return-1;
|
|
||||||
}else{
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hasPermission(name: string,adminOverride=true): boolean{
|
hasPermission(name: string, adminOverride = true): boolean {
|
||||||
if(this.deny){
|
if (this.deny) {
|
||||||
console.warn(
|
console.warn(
|
||||||
"This function may of been used in error, think about using getPermision instead"
|
"This function may of been used in error, think about using getPermision instead",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if(this.getPermissionbit(Permissions.permisions.indexOf(name), this.allow))
|
if (this.getPermissionbit(Permissions.permisions.indexOf(name), this.allow)) return true;
|
||||||
return true;
|
if (name !== "ADMINISTRATOR" && adminOverride) return this.hasPermission("ADMINISTRATOR");
|
||||||
if(name !== "ADMINISTRATOR"&&adminOverride)return this.hasPermission("ADMINISTRATOR");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
setPermission(name: string, setto: number): void{
|
setPermission(name: string, setto: number): void {
|
||||||
const bit = Permissions.permisions.indexOf(name);
|
const bit = Permissions.permisions.indexOf(name);
|
||||||
if(bit===undefined){
|
if (bit === undefined) {
|
||||||
return console.error(
|
return console.error(
|
||||||
"Tried to set permission to " +
|
"Tried to set permission to " + setto + " for " + name + " but it doesn't exist",
|
||||||
setto +
|
|
||||||
" for " +
|
|
||||||
name +
|
|
||||||
" but it doesn't exist"
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(setto === 0){
|
if (setto === 0) {
|
||||||
this.deny = this.setPermissionbit(bit, false, this.deny);
|
this.deny = this.setPermissionbit(bit, false, this.deny);
|
||||||
this.allow = this.setPermissionbit(bit, false, this.allow);
|
this.allow = this.setPermissionbit(bit, false, this.allow);
|
||||||
}else if(setto === 1){
|
} else if (setto === 1) {
|
||||||
this.deny = this.setPermissionbit(bit, false, this.deny);
|
this.deny = this.setPermissionbit(bit, false, this.deny);
|
||||||
this.allow = this.setPermissionbit(bit, true, this.allow);
|
this.allow = this.setPermissionbit(bit, true, this.allow);
|
||||||
}else if(setto === -1){
|
} else if (setto === -1) {
|
||||||
this.deny = this.setPermissionbit(bit, true, this.deny);
|
this.deny = this.setPermissionbit(bit, true, this.deny);
|
||||||
this.allow = this.setPermissionbit(bit, false, this.allow);
|
this.allow = this.setPermissionbit(bit, false, this.allow);
|
||||||
}else{
|
} else {
|
||||||
console.error("invalid number entered:" + setto);
|
console.error("invalid number entered:" + setto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export{ Permissions };
|
export {Permissions};
|
||||||
|
|
|
@ -1,16 +1,25 @@
|
||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Jank Client</title>
|
<title>Jank Client</title>
|
||||||
<meta content="Jank Client" property="og:title">
|
<meta content="Jank Client" property="og:title" />
|
||||||
<meta content="A spacebar client that has DMs, replying and more" property="og:description">
|
<meta content="A spacebar client that has DMs, replying and more" property="og:description" />
|
||||||
<meta content="/logo.webp" property="og:image">
|
<meta content="/logo.webp" property="og:image" />
|
||||||
<meta content="#4b458c" data-react-helmet="true" name="theme-color">
|
<meta content="#4b458c" data-react-helmet="true" name="theme-color" />
|
||||||
<link href="/style.css" rel="stylesheet">
|
<link href="/style.css" rel="stylesheet" />
|
||||||
<link href="/themes.css" rel="stylesheet" id="lightcss">
|
<link href="/themes.css" rel="stylesheet" id="lightcss" />
|
||||||
<style>body.no-theme{background:#16191b;}@media(prefers-color-scheme:light){body.no-theme{background:#9397bd;}}</style>
|
<style>
|
||||||
|
body.no-theme {
|
||||||
|
background: #16191b;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
body.no-theme {
|
||||||
|
background: #9397bd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="no-theme">
|
<body class="no-theme">
|
||||||
<div id="logindiv">
|
<div id="logindiv">
|
||||||
|
@ -19,41 +28,53 @@
|
||||||
<div>
|
<div>
|
||||||
<label for="instance" id="instanceField"><b>Instance:</b></label>
|
<label for="instance" id="instanceField"><b>Instance:</b></label>
|
||||||
<p id="verify"></p>
|
<p id="verify"></p>
|
||||||
<input type="search" list="instances" placeholder="Instance URL" id="instancein" name="instance" value="" required>
|
<input
|
||||||
|
type="search"
|
||||||
|
list="instances"
|
||||||
|
placeholder="Instance URL"
|
||||||
|
id="instancein"
|
||||||
|
name="instance"
|
||||||
|
value=""
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="uname" id="emailField"><b>Email:</b></label>
|
<label for="uname" id="emailField"><b>Email:</b></label>
|
||||||
<input type="text" placeholder="Enter Email" name="uname" id="uname" required>
|
<input type="text" placeholder="Enter Email" name="uname" id="uname" required />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="uname" id="userField"><b>Username:</b></label>
|
<label for="uname" id="userField"><b>Username:</b></label>
|
||||||
<input type="text" placeholder="Enter Username" name="username" id="username" required>
|
<input type="text" placeholder="Enter Username" name="username" id="username" required />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="psw" id="pwField"><b>Password:</b></label>
|
<label for="psw" id="pwField"><b>Password:</b></label>
|
||||||
<input type="password" placeholder="Enter Password" name="psw" id="psw" required>
|
<input type="password" placeholder="Enter Password" name="psw" id="psw" required />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="psw2" id="pw2Field"><b>Enter password again:</b></label>
|
<label for="psw2" id="pw2Field"><b>Enter password again:</b></label>
|
||||||
<input type="password" placeholder="Enter Password Again" name="psw2" id="psw2" required>
|
<input
|
||||||
|
type="password"
|
||||||
|
placeholder="Enter Password Again"
|
||||||
|
name="psw2"
|
||||||
|
id="psw2"
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="date" id="dobField"><b>Date of birth:</b></label>
|
<label for="date" id="dobField"><b>Date of birth:</b></label>
|
||||||
<input type="date" id="date" name="date">
|
<input type="date" id="date" name="date" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<b id="TOSbox">I agree to the <a href="" id="TOSa">Terms of Service</a>:</b>
|
<b id="TOSbox">I agree to the <a href="" id="TOSa">Terms of Service</a>:</b>
|
||||||
<input type="checkbox" id="TOS" name="TOS">
|
<input type="checkbox" id="TOS" name="TOS" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="wrongred" id="wrong"></p>
|
<p class="wrongred" id="wrong"></p>
|
||||||
<div id="h-captcha">
|
<div id="h-captcha"></div>
|
||||||
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="dontgrow" id="createAccount">Create account</button>
|
<button type="submit" class="dontgrow" id="createAccount">Create account</button>
|
||||||
</form>
|
</form>
|
||||||
<a href="/login.html" id="switch" id="alreadyHave">Already have an account?</a>
|
<a href="/login.html" id="switch" id="alreadyHave">Already have an account?</a>
|
||||||
|
|
|
@ -1,31 +1,30 @@
|
||||||
import { I18n } from "./i18n.js";
|
import {I18n} from "./i18n.js";
|
||||||
import{ checkInstance }from"./utils/utils.js";
|
import {checkInstance} from "./utils/utils.js";
|
||||||
import {adduser} from"./login.js";
|
import {adduser} from "./login.js";
|
||||||
import { MarkDown } from "./markdown.js";
|
import {MarkDown} from "./markdown.js";
|
||||||
await I18n.done
|
await I18n.done;
|
||||||
const registerElement = document.getElementById("register");
|
const registerElement = document.getElementById("register");
|
||||||
if(registerElement){
|
if (registerElement) {
|
||||||
registerElement.addEventListener("submit", registertry);
|
registerElement.addEventListener("submit", registertry);
|
||||||
}
|
}
|
||||||
(async ()=>{
|
(async () => {
|
||||||
await I18n.done;
|
await I18n.done;
|
||||||
const userField=document.getElementById("userField");
|
const userField = document.getElementById("userField");
|
||||||
const pw2Field=document.getElementById("pw2Field");
|
const pw2Field = document.getElementById("pw2Field");
|
||||||
const dobField=document.getElementById("dobField");
|
const dobField = document.getElementById("dobField");
|
||||||
const createAccount=document.getElementById("createAccount");
|
const createAccount = document.getElementById("createAccount");
|
||||||
const alreadyHave=document.getElementById("alreadyHave");
|
const alreadyHave = document.getElementById("alreadyHave");
|
||||||
if(userField&&pw2Field&&alreadyHave&&createAccount&&dobField){
|
if (userField && pw2Field && alreadyHave && createAccount && dobField) {
|
||||||
userField.textContent=I18n.getTranslation("htmlPages.userField")
|
userField.textContent = I18n.getTranslation("htmlPages.userField");
|
||||||
pw2Field.textContent=I18n.getTranslation("htmlPages.pw2Field")
|
pw2Field.textContent = I18n.getTranslation("htmlPages.pw2Field");
|
||||||
dobField.textContent=I18n.getTranslation("htmlPages.dobField")
|
dobField.textContent = I18n.getTranslation("htmlPages.dobField");
|
||||||
createAccount.textContent=I18n.getTranslation("htmlPages.createAccount")
|
createAccount.textContent = I18n.getTranslation("htmlPages.createAccount");
|
||||||
alreadyHave.textContent=I18n.getTranslation("htmlPages.alreadyHave")
|
alreadyHave.textContent = I18n.getTranslation("htmlPages.alreadyHave");
|
||||||
}
|
}
|
||||||
})()
|
})();
|
||||||
async function registertry(e: Event){
|
async function registertry(e: Event) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const elements = (e.target as HTMLFormElement)
|
const elements = (e.target as HTMLFormElement).elements as HTMLFormControlsCollection;
|
||||||
.elements as HTMLFormControlsCollection;
|
|
||||||
const email = (elements[1] as HTMLInputElement).value;
|
const email = (elements[1] as HTMLInputElement).value;
|
||||||
const username = (elements[2] as HTMLInputElement).value;
|
const username = (elements[2] as HTMLInputElement).value;
|
||||||
const password = (elements[3] as HTMLInputElement).value;
|
const password = (elements[3] as HTMLInputElement).value;
|
||||||
|
@ -34,15 +33,17 @@ async function registertry(e: Event){
|
||||||
const consent = (elements[6] as HTMLInputElement).checked;
|
const consent = (elements[6] as HTMLInputElement).checked;
|
||||||
const captchaKey = (elements[7] as HTMLInputElement)?.value;
|
const captchaKey = (elements[7] as HTMLInputElement)?.value;
|
||||||
|
|
||||||
if(password !== confirmPassword){
|
if (password !== confirmPassword) {
|
||||||
(document.getElementById("wrong") as HTMLElement).textContent = I18n.getTranslation("localuser.PasswordsNoMatch");
|
(document.getElementById("wrong") as HTMLElement).textContent = I18n.getTranslation(
|
||||||
|
"localuser.PasswordsNoMatch",
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const instanceInfo = JSON.parse(localStorage.getItem("instanceinfo") ?? "{}");
|
const instanceInfo = JSON.parse(localStorage.getItem("instanceinfo") ?? "{}");
|
||||||
const apiurl = new URL(instanceInfo.api);
|
const apiurl = new URL(instanceInfo.api);
|
||||||
|
|
||||||
try{
|
try {
|
||||||
const response = await fetch(apiurl + "/auth/register", {
|
const response = await fetch(apiurl + "/auth/register", {
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
date_of_birth: dateofbirth,
|
date_of_birth: dateofbirth,
|
||||||
|
@ -60,9 +61,9 @@ async function registertry(e: Event){
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if(data.captcha_sitekey){
|
if (data.captcha_sitekey) {
|
||||||
const capt = document.getElementById("h-captcha");
|
const capt = document.getElementById("h-captcha");
|
||||||
if(capt && !capt.children.length){
|
if (capt && !capt.children.length) {
|
||||||
const capty = document.createElement("div");
|
const capty = document.createElement("div");
|
||||||
capty.classList.add("h-captcha");
|
capty.classList.add("h-captcha");
|
||||||
capty.setAttribute("data-sitekey", data.captcha_sitekey);
|
capty.setAttribute("data-sitekey", data.captcha_sitekey);
|
||||||
|
@ -70,15 +71,15 @@ async function registertry(e: Event){
|
||||||
script.src = "https://js.hcaptcha.com/1/api.js";
|
script.src = "https://js.hcaptcha.com/1/api.js";
|
||||||
capt.append(script);
|
capt.append(script);
|
||||||
capt.append(capty);
|
capt.append(capty);
|
||||||
}else{
|
} else {
|
||||||
eval("hcaptcha.reset()");
|
eval("hcaptcha.reset()");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!data.token){
|
if (!data.token) {
|
||||||
handleErrors(data.errors, elements);
|
handleErrors(data.errors, elements);
|
||||||
}else{
|
} else {
|
||||||
adduser({
|
adduser({
|
||||||
serverurls: instanceInfo,
|
serverurls: instanceInfo,
|
||||||
email,
|
email,
|
||||||
|
@ -88,74 +89,72 @@ async function registertry(e: Event){
|
||||||
const redir = new URLSearchParams(window.location.search).get("goback");
|
const redir = new URLSearchParams(window.location.search).get("goback");
|
||||||
window.location.href = redir ? redir : "/channels/@me";
|
window.location.href = redir ? redir : "/channels/@me";
|
||||||
}
|
}
|
||||||
}catch(error){
|
} catch (error) {
|
||||||
console.error("Registration failed:", error);
|
console.error("Registration failed:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleErrors(errors: any, elements: HTMLFormControlsCollection){
|
function handleErrors(errors: any, elements: HTMLFormControlsCollection) {
|
||||||
if(errors.consent){
|
if (errors.consent) {
|
||||||
showError(elements[6] as HTMLElement, errors.consent._errors[0].message);
|
showError(elements[6] as HTMLElement, errors.consent._errors[0].message);
|
||||||
}else if(errors.password){
|
} else if (errors.password) {
|
||||||
showError(
|
showError(
|
||||||
elements[3] as HTMLElement,
|
elements[3] as HTMLElement,
|
||||||
I18n.getTranslation("register.passwordError",errors.password._errors[0].message)
|
I18n.getTranslation("register.passwordError", errors.password._errors[0].message),
|
||||||
);
|
);
|
||||||
}else if(errors.username){
|
} else if (errors.username) {
|
||||||
showError(
|
showError(
|
||||||
elements[2] as HTMLElement,
|
elements[2] as HTMLElement,
|
||||||
I18n.getTranslation("register.usernameError",errors.username._errors[0].message)
|
I18n.getTranslation("register.usernameError", errors.username._errors[0].message),
|
||||||
);
|
);
|
||||||
}else if(errors.email){
|
} else if (errors.email) {
|
||||||
showError(
|
showError(
|
||||||
elements[1] as HTMLElement,
|
elements[1] as HTMLElement,
|
||||||
I18n.getTranslation("register.emailError",errors.email._errors[0].message)
|
I18n.getTranslation("register.emailError", errors.email._errors[0].message),
|
||||||
);
|
);
|
||||||
}else if(errors.date_of_birth){
|
} else if (errors.date_of_birth) {
|
||||||
showError(
|
showError(
|
||||||
elements[5] as HTMLElement,
|
elements[5] as HTMLElement,
|
||||||
I18n.getTranslation("register.DOBError",errors.date_of_birth._errors[0].message)
|
I18n.getTranslation("register.DOBError", errors.date_of_birth._errors[0].message),
|
||||||
);
|
);
|
||||||
}else{
|
} else {
|
||||||
(document.getElementById("wrong") as HTMLElement).textContent =
|
(document.getElementById("wrong") as HTMLElement).textContent =
|
||||||
errors[Object.keys(errors)[0]]._errors[0].message;
|
errors[Object.keys(errors)[0]]._errors[0].message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showError(element: HTMLElement, message: string){
|
function showError(element: HTMLElement, message: string) {
|
||||||
const parent = element.parentElement!;
|
const parent = element.parentElement!;
|
||||||
let errorElement = parent.getElementsByClassName(
|
let errorElement = parent.getElementsByClassName("suberror")[0] as HTMLElement;
|
||||||
"suberror"
|
if (!errorElement) {
|
||||||
)[0] as HTMLElement;
|
|
||||||
if(!errorElement){
|
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
div.classList.add("suberror", "suberrora");
|
div.classList.add("suberror", "suberrora");
|
||||||
parent.append(div);
|
parent.append(div);
|
||||||
errorElement = div;
|
errorElement = div;
|
||||||
}else{
|
} else {
|
||||||
errorElement.classList.remove("suberror");
|
errorElement.classList.remove("suberror");
|
||||||
setTimeout(()=>{
|
setTimeout(() => {
|
||||||
errorElement.classList.add("suberror");
|
errorElement.classList.add("suberror");
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
errorElement.textContent = message;
|
errorElement.textContent = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function tosLogic(){
|
async function tosLogic() {
|
||||||
const instanceInfo = JSON.parse(localStorage.getItem("instanceinfo") ?? "{}");
|
const instanceInfo = JSON.parse(localStorage.getItem("instanceinfo") ?? "{}");
|
||||||
const apiurl = new URL(instanceInfo.api);
|
const apiurl = new URL(instanceInfo.api);
|
||||||
const urlstr=apiurl.toString();
|
const urlstr = apiurl.toString();
|
||||||
const response = await fetch(urlstr + (urlstr.endsWith("/") ? "" : "/") + "ping");
|
const response = await fetch(urlstr + (urlstr.endsWith("/") ? "" : "/") + "ping");
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
const tosPage = data.instance.tosPage;
|
const tosPage = data.instance.tosPage;
|
||||||
|
|
||||||
if(tosPage){
|
if (tosPage) {
|
||||||
const box=document.getElementById("TOSbox");
|
const box = document.getElementById("TOSbox");
|
||||||
if(!box) return;
|
if (!box) return;
|
||||||
box.innerHTML ="";
|
box.innerHTML = "";
|
||||||
box.append(new MarkDown(I18n.getTranslation("register.agreeTOS",tosPage)).makeHTML());
|
box.append(new MarkDown(I18n.getTranslation("register.agreeTOS", tosPage)).makeHTML());
|
||||||
}else{
|
} else {
|
||||||
document.getElementById("TOSbox")!.textContent =I18n.getTranslation("register.noTOS");
|
document.getElementById("TOSbox")!.textContent = I18n.getTranslation("register.noTOS");
|
||||||
}
|
}
|
||||||
console.log(tosPage);
|
console.log(tosPage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import{ Permissions }from"./permissions.js";
|
import {Permissions} from "./permissions.js";
|
||||||
import{ Localuser }from"./localuser.js";
|
import {Localuser} from "./localuser.js";
|
||||||
import{ Guild }from"./guild.js";
|
import {Guild} from "./guild.js";
|
||||||
import{ SnowFlake }from"./snowflake.js";
|
import {SnowFlake} from "./snowflake.js";
|
||||||
import{ rolesjson }from"./jsontypes.js";
|
import {rolesjson} from "./jsontypes.js";
|
||||||
import{ Search }from"./search.js";
|
import {Search} from "./search.js";
|
||||||
class Role extends SnowFlake{
|
class Role extends SnowFlake {
|
||||||
permissions: Permissions;
|
permissions: Permissions;
|
||||||
owner: Guild;
|
owner: Guild;
|
||||||
color!: number;
|
color!: number;
|
||||||
|
@ -14,14 +14,14 @@ class Role extends SnowFlake{
|
||||||
icon!: string;
|
icon!: string;
|
||||||
mentionable!: boolean;
|
mentionable!: boolean;
|
||||||
unicode_emoji!: string;
|
unicode_emoji!: string;
|
||||||
position!:number;
|
position!: number;
|
||||||
headers: Guild["headers"];
|
headers: Guild["headers"];
|
||||||
constructor(json: rolesjson, owner: Guild){
|
constructor(json: rolesjson, owner: Guild) {
|
||||||
super(json.id);
|
super(json.id);
|
||||||
this.headers = owner.headers;
|
this.headers = owner.headers;
|
||||||
this.info = owner.info;
|
this.info = owner.info;
|
||||||
for(const thing of Object.keys(json)){
|
for (const thing of Object.keys(json)) {
|
||||||
if(thing === "id"){
|
if (thing === "id") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
(this as any)[thing] = (json as any)[thing];
|
(this as any)[thing] = (json as any)[thing];
|
||||||
|
@ -29,39 +29,39 @@ class Role extends SnowFlake{
|
||||||
this.permissions = new Permissions(json.permissions);
|
this.permissions = new Permissions(json.permissions);
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
}
|
}
|
||||||
newJson(json: rolesjson){
|
newJson(json: rolesjson) {
|
||||||
for(const thing of Object.keys(json)){
|
for (const thing of Object.keys(json)) {
|
||||||
if(thing === "id"||thing==="permissions"){
|
if (thing === "id" || thing === "permissions") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
(this as any)[thing] = (json as any)[thing];
|
(this as any)[thing] = (json as any)[thing];
|
||||||
}
|
}
|
||||||
this.permissions.allow=BigInt(json.permissions);
|
this.permissions.allow = BigInt(json.permissions);
|
||||||
}
|
}
|
||||||
get guild(): Guild{
|
get guild(): Guild {
|
||||||
return this.owner;
|
return this.owner;
|
||||||
}
|
}
|
||||||
get localuser(): Localuser{
|
get localuser(): Localuser {
|
||||||
return this.guild.localuser;
|
return this.guild.localuser;
|
||||||
}
|
}
|
||||||
getColor(): string | null{
|
getColor(): string | null {
|
||||||
if(this.color === 0){
|
if (this.color === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return`#${this.color.toString(16)}`;
|
return `#${this.color.toString(16)}`;
|
||||||
}
|
}
|
||||||
canManage(){
|
canManage() {
|
||||||
if(this.guild.member.hasPermission("MANAGE_ROLES")){
|
if (this.guild.member.hasPermission("MANAGE_ROLES")) {
|
||||||
let max=-Infinity;
|
let max = -Infinity;
|
||||||
this.guild.member.roles.forEach(r=>max=Math.max(max,r.position))
|
this.guild.member.roles.forEach((r) => (max = Math.max(max, r.position)));
|
||||||
return this.position<=max||this.guild.properties.owner_id===this.guild.member.id;
|
return this.position <= max || this.guild.properties.owner_id === this.guild.member.id;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export{ Role };
|
export {Role};
|
||||||
import{ Options }from"./settings.js";
|
import {Options} from "./settings.js";
|
||||||
class PermissionToggle implements OptionsElement<number>{
|
class PermissionToggle implements OptionsElement<number> {
|
||||||
readonly rolejson: {
|
readonly rolejson: {
|
||||||
name: string;
|
name: string;
|
||||||
readableName: string;
|
readableName: string;
|
||||||
|
@ -70,17 +70,13 @@ class PermissionToggle implements OptionsElement<number>{
|
||||||
permissions: Permissions;
|
permissions: Permissions;
|
||||||
owner: Options;
|
owner: Options;
|
||||||
value!: number;
|
value!: number;
|
||||||
constructor(
|
constructor(roleJSON: PermissionToggle["rolejson"], permissions: Permissions, owner: Options) {
|
||||||
roleJSON: PermissionToggle["rolejson"],
|
|
||||||
permissions: Permissions,
|
|
||||||
owner: Options
|
|
||||||
){
|
|
||||||
this.rolejson = roleJSON;
|
this.rolejson = roleJSON;
|
||||||
this.permissions = permissions;
|
this.permissions = permissions;
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
}
|
}
|
||||||
watchForChange(){}
|
watchForChange() {}
|
||||||
generateHTML(): HTMLElement{
|
generateHTML(): HTMLElement {
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
div.classList.add("setting");
|
div.classList.add("setting");
|
||||||
const name = document.createElement("span");
|
const name = document.createElement("span");
|
||||||
|
@ -94,7 +90,7 @@ class PermissionToggle implements OptionsElement<number>{
|
||||||
div.appendChild(p);
|
div.appendChild(p);
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
generateCheckbox(): HTMLElement{
|
generateCheckbox(): HTMLElement {
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
div.classList.add("tritoggle");
|
div.classList.add("tritoggle");
|
||||||
const state = this.permissions.getPermission(this.rolejson.name);
|
const state = this.permissions.getPermission(this.rolejson.name);
|
||||||
|
@ -103,10 +99,10 @@ class PermissionToggle implements OptionsElement<number>{
|
||||||
on.type = "radio";
|
on.type = "radio";
|
||||||
on.name = this.rolejson.name;
|
on.name = this.rolejson.name;
|
||||||
div.append(on);
|
div.append(on);
|
||||||
if(state === 1){
|
if (state === 1) {
|
||||||
on.checked = true;
|
on.checked = true;
|
||||||
}
|
}
|
||||||
on.onclick = _=>{
|
on.onclick = (_) => {
|
||||||
this.permissions.setPermission(this.rolejson.name, 1);
|
this.permissions.setPermission(this.rolejson.name, 1);
|
||||||
this.owner.changed();
|
this.owner.changed();
|
||||||
};
|
};
|
||||||
|
@ -115,283 +111,293 @@ class PermissionToggle implements OptionsElement<number>{
|
||||||
no.type = "radio";
|
no.type = "radio";
|
||||||
no.name = this.rolejson.name;
|
no.name = this.rolejson.name;
|
||||||
div.append(no);
|
div.append(no);
|
||||||
if(state === 0){
|
if (state === 0) {
|
||||||
no.checked = true;
|
no.checked = true;
|
||||||
}
|
}
|
||||||
no.onclick = _=>{
|
no.onclick = (_) => {
|
||||||
this.permissions.setPermission(this.rolejson.name, 0);
|
this.permissions.setPermission(this.rolejson.name, 0);
|
||||||
this.owner.changed();
|
this.owner.changed();
|
||||||
};
|
};
|
||||||
if(this.permissions.hasDeny){
|
if (this.permissions.hasDeny) {
|
||||||
const off = document.createElement("input");
|
const off = document.createElement("input");
|
||||||
off.type = "radio";
|
off.type = "radio";
|
||||||
off.name = this.rolejson.name;
|
off.name = this.rolejson.name;
|
||||||
div.append(off);
|
div.append(off);
|
||||||
if(state === -1){
|
if (state === -1) {
|
||||||
off.checked = true;
|
off.checked = true;
|
||||||
}
|
}
|
||||||
off.onclick = _=>{
|
off.onclick = (_) => {
|
||||||
this.permissions.setPermission(this.rolejson.name, -1);
|
this.permissions.setPermission(this.rolejson.name, -1);
|
||||||
this.owner.changed();
|
this.owner.changed();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
submit(){}
|
submit() {}
|
||||||
}
|
}
|
||||||
import{ OptionsElement, Buttons }from"./settings.js";
|
import {OptionsElement, Buttons} from "./settings.js";
|
||||||
import { Contextmenu } from "./contextmenu.js";
|
import {Contextmenu} from "./contextmenu.js";
|
||||||
import { Channel } from "./channel.js";
|
import {Channel} from "./channel.js";
|
||||||
import { I18n } from "./i18n.js";
|
import {I18n} from "./i18n.js";
|
||||||
class RoleList extends Buttons{
|
class RoleList extends Buttons {
|
||||||
permissions: [Role, Permissions][];
|
permissions: [Role, Permissions][];
|
||||||
permission: Permissions;
|
permission: Permissions;
|
||||||
readonly guild: Guild;
|
readonly guild: Guild;
|
||||||
readonly channel: false|Channel;
|
readonly channel: false | Channel;
|
||||||
declare buttons: [string, string][];
|
declare buttons: [string, string][];
|
||||||
readonly options: Options;
|
readonly options: Options;
|
||||||
onchange: Function;
|
onchange: Function;
|
||||||
curid?: string;
|
curid?: string;
|
||||||
get info(){
|
get info() {
|
||||||
return this.guild.info;
|
return this.guild.info;
|
||||||
}
|
}
|
||||||
get headers(){
|
get headers() {
|
||||||
return this.guild.headers;
|
return this.guild.headers;
|
||||||
}
|
}
|
||||||
constructor(permissions:[Role, Permissions][], guild:Guild, onchange:Function, channel:false|Channel){
|
constructor(
|
||||||
|
permissions: [Role, Permissions][],
|
||||||
|
guild: Guild,
|
||||||
|
onchange: Function,
|
||||||
|
channel: false | Channel,
|
||||||
|
) {
|
||||||
super("");
|
super("");
|
||||||
this.guild = guild;
|
this.guild = guild;
|
||||||
this.permissions = permissions;
|
this.permissions = permissions;
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
this.onchange = onchange;
|
this.onchange = onchange;
|
||||||
const options = new Options("", this);
|
const options = new Options("", this);
|
||||||
if(channel){
|
if (channel) {
|
||||||
this.permission = new Permissions("0", "0");
|
this.permission = new Permissions("0", "0");
|
||||||
}else{
|
} else {
|
||||||
this.permission = new Permissions("0");
|
this.permission = new Permissions("0");
|
||||||
}
|
}
|
||||||
this.makeguildmenus(options);
|
this.makeguildmenus(options);
|
||||||
for(const thing of Permissions.info()){
|
for (const thing of Permissions.info()) {
|
||||||
options.options.push(
|
options.options.push(new PermissionToggle(thing, this.permission, options));
|
||||||
new PermissionToggle(thing, this.permission, options)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
for(const i of permissions){
|
for (const i of permissions) {
|
||||||
this.buttons.push([i[0].name, i[0].id]);
|
this.buttons.push([i[0].name, i[0].id]);
|
||||||
}
|
}
|
||||||
this.options = options;
|
this.options = options;
|
||||||
guild.roleUpdate=this.groleUpdate.bind(this);
|
guild.roleUpdate = this.groleUpdate.bind(this);
|
||||||
if(channel){
|
if (channel) {
|
||||||
channel.croleUpdate=this.croleUpdate.bind(this);
|
channel.croleUpdate = this.croleUpdate.bind(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private groleUpdate(role:Role,added:1|0|-1){
|
private groleUpdate(role: Role, added: 1 | 0 | -1) {
|
||||||
if(!this.channel){
|
if (!this.channel) {
|
||||||
if(added===1){
|
if (added === 1) {
|
||||||
this.permissions.push([role,role.permissions]);
|
this.permissions.push([role, role.permissions]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(added===-1){
|
if (added === -1) {
|
||||||
this.permissions=this.permissions.filter(r=>r[0]!==role);
|
this.permissions = this.permissions.filter((r) => r[0] !== role);
|
||||||
}
|
}
|
||||||
this.redoButtons();
|
this.redoButtons();
|
||||||
}
|
}
|
||||||
private croleUpdate(role:Role,perm:Permissions,added:boolean){
|
private croleUpdate(role: Role, perm: Permissions, added: boolean) {
|
||||||
if(added){
|
if (added) {
|
||||||
this.permissions.push([role,perm])
|
this.permissions.push([role, perm]);
|
||||||
}else{
|
} else {
|
||||||
this.permissions=this.permissions.filter(r=>r[0]!==role);
|
this.permissions = this.permissions.filter((r) => r[0] !== role);
|
||||||
}
|
}
|
||||||
this.redoButtons();
|
this.redoButtons();
|
||||||
}
|
}
|
||||||
makeguildmenus(option:Options){
|
makeguildmenus(option: Options) {
|
||||||
option.addButtonInput("",I18n.getTranslation("role.displaySettings"),()=>{
|
option.addButtonInput("", I18n.getTranslation("role.displaySettings"), () => {
|
||||||
const role=this.guild.roleids.get(this.curid as string);
|
const role = this.guild.roleids.get(this.curid as string);
|
||||||
if(!role) return;
|
if (!role) return;
|
||||||
const form=option.addSubForm(I18n.getTranslation("role.displaySettings"),()=>{},{
|
const form = option.addSubForm(I18n.getTranslation("role.displaySettings"), () => {}, {
|
||||||
fetchURL:this.info.api+"/guilds/"+this.guild.id+"/roles/"+this.curid,
|
fetchURL: this.info.api + "/guilds/" + this.guild.id + "/roles/" + this.curid,
|
||||||
method:"PATCH",
|
method: "PATCH",
|
||||||
headers:this.headers,
|
headers: this.headers,
|
||||||
traditionalSubmit:true
|
traditionalSubmit: true,
|
||||||
});
|
});
|
||||||
form.addTextInput(I18n.getTranslation("role.name"),"name",{
|
form.addTextInput(I18n.getTranslation("role.name"), "name", {
|
||||||
initText:role.name
|
initText: role.name,
|
||||||
});
|
});
|
||||||
form.addCheckboxInput(I18n.getTranslation("role.hoisted"),"hoist",{
|
form.addCheckboxInput(I18n.getTranslation("role.hoisted"), "hoist", {
|
||||||
initState:role.hoist
|
initState: role.hoist,
|
||||||
});
|
});
|
||||||
form.addCheckboxInput(I18n.getTranslation("role.mentionable"),"mentionable",{
|
form.addCheckboxInput(I18n.getTranslation("role.mentionable"), "mentionable", {
|
||||||
initState:role.mentionable
|
initState: role.mentionable,
|
||||||
});
|
});
|
||||||
const color="#"+role.color.toString(16).padStart(6,"0");
|
const color = "#" + role.color.toString(16).padStart(6, "0");
|
||||||
form.addColorInput(I18n.getTranslation("role.color"),"color",{
|
form.addColorInput(I18n.getTranslation("role.color"), "color", {
|
||||||
initColor:color
|
initColor: color,
|
||||||
});
|
});
|
||||||
form.addPreprocessor((obj:any)=>{
|
form.addPreprocessor((obj: any) => {
|
||||||
obj.color=Number("0x"+obj.color.substring(1));
|
obj.color = Number("0x" + obj.color.substring(1));
|
||||||
console.log(obj.color);
|
console.log(obj.color);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
static channelrolemenu=this.ChannelRoleMenu();
|
static channelrolemenu = this.ChannelRoleMenu();
|
||||||
static guildrolemenu=this.GuildRoleMenu();
|
static guildrolemenu = this.GuildRoleMenu();
|
||||||
private static ChannelRoleMenu(){
|
private static ChannelRoleMenu() {
|
||||||
const menu=new Contextmenu<RoleList,Role>("role settings");
|
const menu = new Contextmenu<RoleList, Role>("role settings");
|
||||||
menu.addbutton(()=>I18n.getTranslation("role.remove"),function(role){
|
menu.addbutton(
|
||||||
if(!this.channel) return;
|
() => I18n.getTranslation("role.remove"),
|
||||||
|
function (role) {
|
||||||
|
if (!this.channel) return;
|
||||||
console.log(role);
|
console.log(role);
|
||||||
fetch(this.info.api+"/channels/"+this.channel.id+"/permissions/"+role.id,{
|
fetch(this.info.api + "/channels/" + this.channel.id + "/permissions/" + role.id, {
|
||||||
method:"DELETE",
|
method: "DELETE",
|
||||||
headers:this.headers
|
headers: this.headers,
|
||||||
})
|
});
|
||||||
},null);
|
},
|
||||||
|
null,
|
||||||
|
);
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
private static GuildRoleMenu(){
|
private static GuildRoleMenu() {
|
||||||
const menu=new Contextmenu<RoleList,Role>("role settings");
|
const menu = new Contextmenu<RoleList, Role>("role settings");
|
||||||
menu.addbutton(()=>I18n.getTranslation("role.delete"),function(role){
|
menu.addbutton(
|
||||||
if(!confirm(I18n.getTranslation("role.confirmDelete"))) return;
|
() => I18n.getTranslation("role.delete"),
|
||||||
|
function (role) {
|
||||||
|
if (!confirm(I18n.getTranslation("role.confirmDelete"))) return;
|
||||||
console.log(role);
|
console.log(role);
|
||||||
fetch(this.info.api+"/guilds/"+this.guild.id+"/roles/"+role.id,{
|
fetch(this.info.api + "/guilds/" + this.guild.id + "/roles/" + role.id, {
|
||||||
method:"DELETE",
|
method: "DELETE",
|
||||||
headers:this.headers
|
headers: this.headers,
|
||||||
})
|
});
|
||||||
},null);
|
},
|
||||||
|
null,
|
||||||
|
);
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
redoButtons(){
|
redoButtons() {
|
||||||
this.buttons=[];
|
this.buttons = [];
|
||||||
this.permissions.sort(([a],[b])=>b.position-a.position);
|
this.permissions.sort(([a], [b]) => b.position - a.position);
|
||||||
for(const i of this.permissions){
|
for (const i of this.permissions) {
|
||||||
this.buttons.push([i[0].name, i[0].id]);
|
this.buttons.push([i[0].name, i[0].id]);
|
||||||
}
|
}
|
||||||
console.log("in here :P")
|
|
||||||
if(!this.buttonList)return;
|
|
||||||
console.log("in here :P");
|
console.log("in here :P");
|
||||||
const elms=Array.from(this.buttonList.children);
|
if (!this.buttonList) return;
|
||||||
const div=elms[0] as HTMLDivElement;
|
console.log("in here :P");
|
||||||
const div2=elms[1] as HTMLDivElement;
|
const elms = Array.from(this.buttonList.children);
|
||||||
|
const div = elms[0] as HTMLDivElement;
|
||||||
|
const div2 = elms[1] as HTMLDivElement;
|
||||||
console.log(div);
|
console.log(div);
|
||||||
div.innerHTML="";
|
div.innerHTML = "";
|
||||||
div.append(this.buttonListGen(div2));//not actually sure why the html is needed
|
div.append(this.buttonListGen(div2)); //not actually sure why the html is needed
|
||||||
}
|
}
|
||||||
buttonMap=new WeakMap<HTMLButtonElement,Role>();
|
buttonMap = new WeakMap<HTMLButtonElement, Role>();
|
||||||
dragged?:HTMLButtonElement;
|
dragged?: HTMLButtonElement;
|
||||||
buttonDragEvents(button:HTMLButtonElement,role:Role){
|
buttonDragEvents(button: HTMLButtonElement, role: Role) {
|
||||||
this.buttonMap.set(button,role);
|
this.buttonMap.set(button, role);
|
||||||
button.addEventListener("dragstart", e=>{
|
button.addEventListener("dragstart", (e) => {
|
||||||
this.dragged = button;
|
this.dragged = button;
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
});
|
});
|
||||||
|
|
||||||
button.addEventListener("dragend", ()=>{
|
button.addEventListener("dragend", () => {
|
||||||
this.dragged = undefined;
|
this.dragged = undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
button.addEventListener("dragenter", event=>{
|
button.addEventListener("dragenter", (event) => {
|
||||||
console.log("enter");
|
console.log("enter");
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
button.addEventListener("dragover", event=>{
|
button.addEventListener("dragover", (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
button.addEventListener("drop", _=>{
|
button.addEventListener("drop", (_) => {
|
||||||
const role2=this.buttonMap.get(this.dragged as HTMLButtonElement);
|
const role2 = this.buttonMap.get(this.dragged as HTMLButtonElement);
|
||||||
if(!role2) return;
|
if (!role2) return;
|
||||||
const index2=this.guild.roles.indexOf(role2);
|
const index2 = this.guild.roles.indexOf(role2);
|
||||||
this.guild.roles.splice(index2,1);
|
this.guild.roles.splice(index2, 1);
|
||||||
const index=this.guild.roles.indexOf(role);
|
const index = this.guild.roles.indexOf(role);
|
||||||
this.guild.roles.splice(index+1,0,role2);
|
this.guild.roles.splice(index + 1, 0, role2);
|
||||||
this.guild.recalcRoles();
|
this.guild.recalcRoles();
|
||||||
console.log(role);
|
console.log(role);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
buttonListGen(html:HTMLElement){
|
buttonListGen(html: HTMLElement) {
|
||||||
const buttonTable=document.createElement("div");
|
const buttonTable = document.createElement("div");
|
||||||
buttonTable.classList.add("flexttb");
|
buttonTable.classList.add("flexttb");
|
||||||
|
|
||||||
const roleRow=document.createElement("div");
|
const roleRow = document.createElement("div");
|
||||||
roleRow.classList.add("flexltr","rolesheader");
|
roleRow.classList.add("flexltr", "rolesheader");
|
||||||
roleRow.append("Roles");
|
roleRow.append("Roles");
|
||||||
const add=document.createElement("span");
|
const add = document.createElement("span");
|
||||||
add.classList.add("svg-plus","svgicon","addrole");
|
add.classList.add("svg-plus", "svgicon", "addrole");
|
||||||
add.onclick=async (e)=>{
|
add.onclick = async (e) => {
|
||||||
const box=add.getBoundingClientRect();
|
const box = add.getBoundingClientRect();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if(this.channel){
|
if (this.channel) {
|
||||||
const roles:[Role,string[]][]=[];
|
const roles: [Role, string[]][] = [];
|
||||||
for(const role of this.guild.roles){
|
for (const role of this.guild.roles) {
|
||||||
if(this.permissions.find(r=>r[0]==role)){
|
if (this.permissions.find((r) => r[0] == role)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
roles.push([role,[role.name]]);
|
roles.push([role, [role.name]]);
|
||||||
}
|
}
|
||||||
const search=new Search(roles);
|
const search = new Search(roles);
|
||||||
|
|
||||||
const found=await search.find(box.left,box.top);
|
const found = await search.find(box.left, box.top);
|
||||||
|
|
||||||
|
if (!found) return;
|
||||||
if(!found) return;
|
|
||||||
console.log(found);
|
console.log(found);
|
||||||
this.onchange(found.id,new Permissions("0","0"));
|
this.onchange(found.id, new Permissions("0", "0"));
|
||||||
}else{
|
} else {
|
||||||
const div=document.createElement("div");
|
const div = document.createElement("div");
|
||||||
const bar=document.createElement("input");
|
const bar = document.createElement("input");
|
||||||
div.classList.add("fixedsearch","OptionList");
|
div.classList.add("fixedsearch", "OptionList");
|
||||||
bar.type="text";
|
bar.type = "text";
|
||||||
div.style.left=(box.left^0)+"px";
|
div.style.left = (box.left ^ 0) + "px";
|
||||||
div.style.top=(box.top^0)+"px";
|
div.style.top = (box.top ^ 0) + "px";
|
||||||
div.append(bar)
|
div.append(bar);
|
||||||
document.body.append(div);
|
document.body.append(div);
|
||||||
if(Contextmenu.currentmenu != ""){
|
if (Contextmenu.currentmenu != "") {
|
||||||
Contextmenu.currentmenu.remove();
|
Contextmenu.currentmenu.remove();
|
||||||
}
|
}
|
||||||
Contextmenu.currentmenu=div;
|
Contextmenu.currentmenu = div;
|
||||||
Contextmenu.keepOnScreen(div);
|
Contextmenu.keepOnScreen(div);
|
||||||
bar.onchange=()=>{
|
bar.onchange = () => {
|
||||||
div.remove();
|
div.remove();
|
||||||
console.log(bar.value)
|
console.log(bar.value);
|
||||||
if(bar.value==="") return;
|
if (bar.value === "") return;
|
||||||
fetch(this.info.api+`/guilds/${this.guild.id}/roles`,{
|
fetch(this.info.api + `/guilds/${this.guild.id}/roles`, {
|
||||||
method:"POST",
|
method: "POST",
|
||||||
headers:this.headers,
|
headers: this.headers,
|
||||||
body:JSON.stringify({
|
body: JSON.stringify({
|
||||||
color:0,
|
color: 0,
|
||||||
name:bar.value,
|
name: bar.value,
|
||||||
permissions:""
|
permissions: "",
|
||||||
})
|
}),
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
roleRow.append(add);
|
roleRow.append(add);
|
||||||
|
|
||||||
buttonTable.append(roleRow);
|
buttonTable.append(roleRow);
|
||||||
for(const thing of this.buttons){
|
for (const thing of this.buttons) {
|
||||||
const button = document.createElement("button");
|
const button = document.createElement("button");
|
||||||
button.classList.add("SettingsButton");
|
button.classList.add("SettingsButton");
|
||||||
button.textContent = thing[0];
|
button.textContent = thing[0];
|
||||||
const role=this.guild.roleids.get(thing[1]);
|
const role = this.guild.roleids.get(thing[1]);
|
||||||
if(role){
|
if (role) {
|
||||||
if(!this.channel){
|
if (!this.channel) {
|
||||||
if(role.canManage()){
|
if (role.canManage()) {
|
||||||
this.buttonDragEvents(button,role);
|
this.buttonDragEvents(button, role);
|
||||||
button.draggable=true;
|
button.draggable = true;
|
||||||
RoleList.guildrolemenu.bindContextmenu(button,this,role)
|
RoleList.guildrolemenu.bindContextmenu(button, this, role);
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
if(role.canManage()){
|
if (role.canManage()) {
|
||||||
RoleList.channelrolemenu.bindContextmenu(button,this,role)
|
RoleList.channelrolemenu.bindContextmenu(button, this, role);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
button.onclick = _=>{
|
button.onclick = (_) => {
|
||||||
this.generateHTMLArea(thing[1], html);
|
this.generateHTMLArea(thing[1], html);
|
||||||
if(this.warndiv){
|
if (this.warndiv) {
|
||||||
this.warndiv.remove();
|
this.warndiv.remove();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -400,29 +406,29 @@ class RoleList extends Buttons{
|
||||||
return buttonTable;
|
return buttonTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
generateButtons(html:HTMLElement):HTMLDivElement{
|
generateButtons(html: HTMLElement): HTMLDivElement {
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
div.classList.add("settingbuttons");
|
div.classList.add("settingbuttons");
|
||||||
div.append(this.buttonListGen(html));
|
div.append(this.buttonListGen(html));
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
handleString(str: string): HTMLElement{
|
handleString(str: string): HTMLElement {
|
||||||
this.curid = str;
|
this.curid = str;
|
||||||
const arr = this.permissions.find(_=>_[0].id === str);
|
const arr = this.permissions.find((_) => _[0].id === str);
|
||||||
if(arr){
|
if (arr) {
|
||||||
const perm = arr[1];
|
const perm = arr[1];
|
||||||
this.permission.deny = perm.deny;
|
this.permission.deny = perm.deny;
|
||||||
this.permission.allow = perm.allow;
|
this.permission.allow = perm.allow;
|
||||||
const role = this.permissions.find(e=>e[0].id === str);
|
const role = this.permissions.find((e) => e[0].id === str);
|
||||||
if(role){
|
if (role) {
|
||||||
this.options.name = role[0].name;
|
this.options.name = role[0].name;
|
||||||
this.options.haschanged = false;
|
this.options.haschanged = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.options.generateHTML();
|
return this.options.generateHTML();
|
||||||
}
|
}
|
||||||
save(){
|
save() {
|
||||||
this.onchange(this.curid, this.permission);
|
this.onchange(this.curid, this.permission);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export{ RoleList, PermissionToggle };
|
export {RoleList, PermissionToggle};
|
||||||
|
|
|
@ -1,72 +1,70 @@
|
||||||
import { Contextmenu } from "./contextmenu.js";
|
import {Contextmenu} from "./contextmenu.js";
|
||||||
|
|
||||||
class Search<E>{
|
class Search<E> {
|
||||||
options:Map<string,E>;
|
options: Map<string, E>;
|
||||||
readonly keys:string[];
|
readonly keys: string[];
|
||||||
constructor(options:[E,string[]][]){
|
constructor(options: [E, string[]][]) {
|
||||||
const map=options.flatMap(e=>{
|
const map = options.flatMap((e) => {
|
||||||
const val=e[1].map(f=>[f,e[0]]);
|
const val = e[1].map((f) => [f, e[0]]);
|
||||||
return val as [string,E][];
|
return val as [string, E][];
|
||||||
})
|
|
||||||
this.options=new Map(map);
|
|
||||||
this.keys=[...this.options.keys()];
|
|
||||||
}
|
|
||||||
generateList(str:string,max:number,res:(e:E)=>void){
|
|
||||||
str=str.toLowerCase();
|
|
||||||
const options=this.keys.filter(e=>{
|
|
||||||
return e.toLowerCase().includes(str)
|
|
||||||
});
|
});
|
||||||
const div=document.createElement("div");
|
this.options = new Map(map);
|
||||||
div.classList.add("OptionList","flexttb");
|
this.keys = [...this.options.keys()];
|
||||||
for(const option of options.slice(0, max)){
|
|
||||||
const hoption=document.createElement("span");
|
|
||||||
hoption.textContent=option;
|
|
||||||
hoption.onclick=()=>{
|
|
||||||
if(!this.options.has(option)) return;
|
|
||||||
res(this.options.get(option) as E)
|
|
||||||
}
|
}
|
||||||
|
generateList(str: string, max: number, res: (e: E) => void) {
|
||||||
|
str = str.toLowerCase();
|
||||||
|
const options = this.keys.filter((e) => {
|
||||||
|
return e.toLowerCase().includes(str);
|
||||||
|
});
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.classList.add("OptionList", "flexttb");
|
||||||
|
for (const option of options.slice(0, max)) {
|
||||||
|
const hoption = document.createElement("span");
|
||||||
|
hoption.textContent = option;
|
||||||
|
hoption.onclick = () => {
|
||||||
|
if (!this.options.has(option)) return;
|
||||||
|
res(this.options.get(option) as E);
|
||||||
|
};
|
||||||
div.append(hoption);
|
div.append(hoption);
|
||||||
}
|
}
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
async find(x:number,y:number,max=4):Promise<E|undefined>{
|
async find(x: number, y: number, max = 4): Promise<E | undefined> {
|
||||||
return new Promise<E|undefined>((res)=>{
|
return new Promise<E | undefined>((res) => {
|
||||||
|
const container = document.createElement("div");
|
||||||
const container=document.createElement("div");
|
|
||||||
container.classList.add("fixedsearch");
|
container.classList.add("fixedsearch");
|
||||||
console.log((x^0)+"",(y^0)+"");
|
console.log((x ^ 0) + "", (y ^ 0) + "");
|
||||||
container.style.left=(x^0)+"px";
|
container.style.left = (x ^ 0) + "px";
|
||||||
container.style.top=(y^0)+"px";
|
container.style.top = (y ^ 0) + "px";
|
||||||
const remove=container.remove;
|
const remove = container.remove;
|
||||||
container.remove=()=>{
|
container.remove = () => {
|
||||||
remove.call(container);
|
remove.call(container);
|
||||||
res(undefined);
|
res(undefined);
|
||||||
}
|
};
|
||||||
|
|
||||||
function resolve(e:E){
|
function resolve(e: E) {
|
||||||
res(e);
|
res(e);
|
||||||
container.remove();
|
container.remove();
|
||||||
}
|
}
|
||||||
const bar=document.createElement("input");
|
const bar = document.createElement("input");
|
||||||
const options=document.createElement("div");
|
const options = document.createElement("div");
|
||||||
const keydown=()=>{
|
const keydown = () => {
|
||||||
const html=this.generateList(bar.value,max,resolve);
|
const html = this.generateList(bar.value, max, resolve);
|
||||||
options.innerHTML="";
|
options.innerHTML = "";
|
||||||
options.append(html);
|
options.append(html);
|
||||||
}
|
};
|
||||||
bar.oninput=keydown;
|
bar.oninput = keydown;
|
||||||
keydown();
|
keydown();
|
||||||
bar.type="text";
|
bar.type = "text";
|
||||||
container.append(bar);
|
container.append(bar);
|
||||||
container.append(options);
|
container.append(options);
|
||||||
document.body.append(container);
|
document.body.append(container);
|
||||||
if(Contextmenu.currentmenu != ""){
|
if (Contextmenu.currentmenu != "") {
|
||||||
Contextmenu.currentmenu.remove();
|
Contextmenu.currentmenu.remove();
|
||||||
}
|
}
|
||||||
Contextmenu.currentmenu=container;
|
Contextmenu.currentmenu = container;
|
||||||
Contextmenu.keepOnScreen(container);
|
Contextmenu.keepOnScreen(container);
|
||||||
|
});
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export {Search};
|
export {Search};
|
||||||
|
|
|
@ -1,42 +1,45 @@
|
||||||
function deleteoldcache(){
|
function deleteoldcache() {
|
||||||
caches.delete("cache");
|
caches.delete("cache");
|
||||||
console.log("this ran :P");
|
console.log("this ran :P");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function putInCache(request: URL | RequestInfo, response: Response){
|
async function putInCache(request: URL | RequestInfo, response: Response) {
|
||||||
console.log(request, response);
|
console.log(request, response);
|
||||||
const cache = await caches.open("cache");
|
const cache = await caches.open("cache");
|
||||||
console.log("Grabbed");
|
console.log("Grabbed");
|
||||||
try{
|
try {
|
||||||
console.log(await cache.put(request, response));
|
console.log(await cache.put(request, response));
|
||||||
}catch(error){
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastcache: string;
|
let lastcache: string;
|
||||||
self.addEventListener("activate", async ()=>{
|
self.addEventListener("activate", async () => {
|
||||||
console.log("Service Worker activated");
|
console.log("Service Worker activated");
|
||||||
checkCache();
|
checkCache();
|
||||||
});
|
});
|
||||||
|
|
||||||
async function checkCache(){
|
async function checkCache() {
|
||||||
if(checkedrecently){
|
if (checkedrecently) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const promise = await caches.match("/getupdates");
|
const promise = await caches.match("/getupdates");
|
||||||
if(promise){
|
if (promise) {
|
||||||
lastcache = await promise.text();
|
lastcache = await promise.text();
|
||||||
}
|
}
|
||||||
console.log(lastcache);
|
console.log(lastcache);
|
||||||
fetch("/getupdates").then(async data=>{
|
fetch("/getupdates").then(async (data) => {
|
||||||
setTimeout((_: any)=>{
|
setTimeout(
|
||||||
|
(_: any) => {
|
||||||
checkedrecently = false;
|
checkedrecently = false;
|
||||||
}, 1000 * 60 * 30);
|
},
|
||||||
if(!data.ok) return;
|
1000 * 60 * 30,
|
||||||
|
);
|
||||||
|
if (!data.ok) return;
|
||||||
const text = await data.clone().text();
|
const text = await data.clone().text();
|
||||||
console.log(text, lastcache);
|
console.log(text, lastcache);
|
||||||
if(lastcache !== text){
|
if (lastcache !== text) {
|
||||||
deleteoldcache();
|
deleteoldcache();
|
||||||
putInCache("/getupdates", data);
|
putInCache("/getupdates", data);
|
||||||
}
|
}
|
||||||
|
@ -45,98 +48,100 @@ async function checkCache(){
|
||||||
}
|
}
|
||||||
var checkedrecently = false;
|
var checkedrecently = false;
|
||||||
|
|
||||||
function samedomain(url: string | URL){
|
function samedomain(url: string | URL) {
|
||||||
return new URL(url).origin === self.origin;
|
return new URL(url).origin === self.origin;
|
||||||
}
|
}
|
||||||
|
|
||||||
const htmlFiles=new Set(["/index","/login","/home","/register","/oauth2/auth"]);
|
const htmlFiles = new Set(["/index", "/login", "/home", "/register", "/oauth2/auth"]);
|
||||||
|
|
||||||
|
function isHtml(url: string): string | void {
|
||||||
function isHtml(url:string):string|void{
|
const path = new URL(url).pathname;
|
||||||
const path=new URL(url).pathname;
|
if (htmlFiles.has(path) || htmlFiles.has(path + ".html")) {
|
||||||
if(htmlFiles.has(path)||htmlFiles.has(path+".html")){
|
return path + path.endsWith(".html") ? "" : ".html";
|
||||||
return path+path.endsWith(".html")?"":".html";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let enabled="false";
|
let enabled = "false";
|
||||||
let offline=false;
|
let offline = false;
|
||||||
|
|
||||||
function toPath(url:string):string{
|
function toPath(url: string): string {
|
||||||
const Url= new URL(url);
|
const Url = new URL(url);
|
||||||
let html=isHtml(url);
|
let html = isHtml(url);
|
||||||
if(!html){
|
if (!html) {
|
||||||
const path=Url.pathname;
|
const path = Url.pathname;
|
||||||
if(path.startsWith("/channels")){
|
if (path.startsWith("/channels")) {
|
||||||
html="./index.html"
|
html = "./index.html";
|
||||||
}else if(path.startsWith("/invite/")||path==="/invite"){
|
} else if (path.startsWith("/invite/") || path === "/invite") {
|
||||||
html="./invite.html"
|
html = "./invite.html";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return html||Url.pathname;
|
return html || Url.pathname;
|
||||||
}
|
}
|
||||||
let fails=0;
|
let fails = 0;
|
||||||
async function getfile(event: FetchEvent):Promise<Response>{
|
async function getfile(event: FetchEvent): Promise<Response> {
|
||||||
checkCache();
|
checkCache();
|
||||||
if(!samedomain(event.request.url)||enabled==="false"||(enabled==="offlineOnly"&&!offline)){
|
if (
|
||||||
const responce=await fetch(event.request.clone());
|
!samedomain(event.request.url) ||
|
||||||
if(samedomain(event.request.url)){
|
enabled === "false" ||
|
||||||
if(enabled==="offlineOnly"&&responce.ok){
|
(enabled === "offlineOnly" && !offline)
|
||||||
putInCache(toPath(event.request.url),responce.clone());
|
) {
|
||||||
|
const responce = await fetch(event.request.clone());
|
||||||
|
if (samedomain(event.request.url)) {
|
||||||
|
if (enabled === "offlineOnly" && responce.ok) {
|
||||||
|
putInCache(toPath(event.request.url), responce.clone());
|
||||||
}
|
}
|
||||||
if(!responce.ok){
|
if (!responce.ok) {
|
||||||
fails++;
|
fails++;
|
||||||
if(fails>5){
|
if (fails > 5) {
|
||||||
offline=true;
|
offline = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return responce;
|
return responce;
|
||||||
}
|
}
|
||||||
|
|
||||||
let path=toPath(event.request.url);
|
let path = toPath(event.request.url);
|
||||||
if(path === "/instances.json"){
|
if (path === "/instances.json") {
|
||||||
return await fetch(path);
|
return await fetch(path);
|
||||||
}
|
}
|
||||||
console.log("Getting path: "+path);
|
console.log("Getting path: " + path);
|
||||||
const responseFromCache = await caches.match(path);
|
const responseFromCache = await caches.match(path);
|
||||||
if(responseFromCache){
|
if (responseFromCache) {
|
||||||
console.log("cache hit");
|
console.log("cache hit");
|
||||||
return responseFromCache;
|
return responseFromCache;
|
||||||
}
|
}
|
||||||
try{
|
try {
|
||||||
const responseFromNetwork = await fetch(path);
|
const responseFromNetwork = await fetch(path);
|
||||||
if(responseFromNetwork.ok){
|
if (responseFromNetwork.ok) {
|
||||||
await putInCache(path, responseFromNetwork.clone());
|
await putInCache(path, responseFromNetwork.clone());
|
||||||
}
|
}
|
||||||
return responseFromNetwork;
|
return responseFromNetwork;
|
||||||
}catch(e){
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
return new Response(null);
|
return new Response(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.addEventListener("fetch", (e) => {
|
||||||
self.addEventListener("fetch", (e)=>{
|
const event = e as FetchEvent;
|
||||||
const event=e as FetchEvent;
|
try {
|
||||||
try{
|
|
||||||
event.respondWith(getfile(event));
|
event.respondWith(getfile(event));
|
||||||
}catch(e){
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
self.addEventListener("message", (message)=>{
|
self.addEventListener("message", (message) => {
|
||||||
const data=message.data;
|
const data = message.data;
|
||||||
switch(data.code){
|
switch (data.code) {
|
||||||
case "setMode":
|
case "setMode":
|
||||||
enabled=data.data;
|
enabled = data.data;
|
||||||
break;
|
break;
|
||||||
case "CheckUpdate":
|
case "CheckUpdate":
|
||||||
checkedrecently=false;
|
checkedrecently = false;
|
||||||
checkCache();
|
checkCache();
|
||||||
break;
|
break;
|
||||||
case "ForceClear":
|
case "ForceClear":
|
||||||
deleteoldcache();
|
deleteoldcache();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,17 +1,17 @@
|
||||||
abstract class SnowFlake{
|
abstract class SnowFlake {
|
||||||
public readonly id: string;
|
public readonly id: string;
|
||||||
constructor(id: string){
|
constructor(id: string) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
getUnixTime(): number{
|
getUnixTime(): number {
|
||||||
return SnowFlake.stringToUnixTime(this.id);
|
return SnowFlake.stringToUnixTime(this.id);
|
||||||
}
|
}
|
||||||
static stringToUnixTime(str: string){
|
static stringToUnixTime(str: string) {
|
||||||
try{
|
try {
|
||||||
return Number((BigInt(str) >> 22n) + 1420070400000n);
|
return Number((BigInt(str) >> 22n) + 1420070400000n);
|
||||||
}catch{
|
} catch {
|
||||||
throw new Error(`The ID is corrupted, it's ${str} when it should be some number.`);
|
throw new Error(`The ID is corrupted, it's ${str} when it should be some number.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export{ SnowFlake };
|
export {SnowFlake};
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,16 +1,16 @@
|
||||||
import{ Member }from"./member.js";
|
import {Member} from "./member.js";
|
||||||
import{ MarkDown }from"./markdown.js";
|
import {MarkDown} from "./markdown.js";
|
||||||
import{ Contextmenu }from"./contextmenu.js";
|
import {Contextmenu} from "./contextmenu.js";
|
||||||
import{ Localuser }from"./localuser.js";
|
import {Localuser} from "./localuser.js";
|
||||||
import{ Guild }from"./guild.js";
|
import {Guild} from "./guild.js";
|
||||||
import{ SnowFlake }from"./snowflake.js";
|
import {SnowFlake} from "./snowflake.js";
|
||||||
import{ presencejson, userjson }from"./jsontypes.js";
|
import {presencejson, userjson} from "./jsontypes.js";
|
||||||
import { Role } from "./role.js";
|
import {Role} from "./role.js";
|
||||||
import { Search } from "./search.js";
|
import {Search} from "./search.js";
|
||||||
import { I18n } from "./i18n.js";
|
import {I18n} from "./i18n.js";
|
||||||
import { Direct } from "./direct.js";
|
import {Direct} from "./direct.js";
|
||||||
|
|
||||||
class User extends SnowFlake{
|
class User extends SnowFlake {
|
||||||
owner: Localuser;
|
owner: Localuser;
|
||||||
hypotheticalpfp!: boolean;
|
hypotheticalpfp!: boolean;
|
||||||
avatar!: string | null;
|
avatar!: string | null;
|
||||||
|
@ -29,33 +29,32 @@ class User extends SnowFlake{
|
||||||
premium_type!: number;
|
premium_type!: number;
|
||||||
theme_colors!: string;
|
theme_colors!: string;
|
||||||
badge_ids!: string[];
|
badge_ids!: string[];
|
||||||
members: WeakMap<Guild, Member | undefined | Promise<Member | undefined>> =
|
members: WeakMap<Guild, Member | undefined | Promise<Member | undefined>> = new WeakMap();
|
||||||
new WeakMap();
|
|
||||||
status!: string;
|
status!: string;
|
||||||
resolving: false | Promise<any> = false;
|
resolving: false | Promise<any> = false;
|
||||||
|
|
||||||
constructor(userjson: userjson, owner: Localuser, dontclone = false){
|
constructor(userjson: userjson, owner: Localuser, dontclone = false) {
|
||||||
super(userjson.id);
|
super(userjson.id);
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
if(localStorage.getItem("logbad")&&owner.user&&owner.user.id!==userjson.id){
|
if (localStorage.getItem("logbad") && owner.user && owner.user.id !== userjson.id) {
|
||||||
this.checkfortmi(userjson)
|
this.checkfortmi(userjson);
|
||||||
}
|
}
|
||||||
if(!owner){
|
if (!owner) {
|
||||||
console.error("missing localuser");
|
console.error("missing localuser");
|
||||||
}
|
}
|
||||||
if(dontclone){
|
if (dontclone) {
|
||||||
for(const key of Object.keys(userjson)){
|
for (const key of Object.keys(userjson)) {
|
||||||
if(key === "bio"){
|
if (key === "bio") {
|
||||||
this.bio = new MarkDown(userjson[key], this.localuser);
|
this.bio = new MarkDown(userjson[key], this.localuser);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if(key === "id"){
|
if (key === "id") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
(this as any)[key] = (userjson as any)[key];
|
(this as any)[key] = (userjson as any)[key];
|
||||||
}
|
}
|
||||||
this.hypotheticalpfp = false;
|
this.hypotheticalpfp = false;
|
||||||
}else{
|
} else {
|
||||||
return User.checkuser(userjson, owner);
|
return User.checkuser(userjson, owner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,18 +63,27 @@ class User extends SnowFlake{
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
checkfortmi(json:any){
|
checkfortmi(json: any) {
|
||||||
if(json.data){
|
if (json.data) {
|
||||||
console.error("Server sent *way* too much info, this is really bad, it sent data")
|
console.error("Server sent *way* too much info, this is really bad, it sent data");
|
||||||
}
|
}
|
||||||
const bad=new Set(["fingerprints", "extended_settings", "mfa_enabled", "nsfw_allowed", "premium_usage_flags", "totp_last_ticket", "totp_secret", "webauthn_enabled"]);
|
const bad = new Set([
|
||||||
for(const thing of bad){
|
"fingerprints",
|
||||||
if(json.hasOwnProperty(thing)){
|
"extended_settings",
|
||||||
console.error(thing+" should not be exposed to the client");
|
"mfa_enabled",
|
||||||
|
"nsfw_allowed",
|
||||||
|
"premium_usage_flags",
|
||||||
|
"totp_last_ticket",
|
||||||
|
"totp_secret",
|
||||||
|
"webauthn_enabled",
|
||||||
|
]);
|
||||||
|
for (const thing of bad) {
|
||||||
|
if (json.hasOwnProperty(thing)) {
|
||||||
|
console.error(thing + " should not be exposed to the client");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tojson():userjson{
|
tojson(): userjson {
|
||||||
return {
|
return {
|
||||||
username: this.username,
|
username: this.username,
|
||||||
id: this.id,
|
id: this.id,
|
||||||
|
@ -91,57 +99,54 @@ class User extends SnowFlake{
|
||||||
theme_colors: this.theme_colors,
|
theme_colors: this.theme_colors,
|
||||||
pronouns: this.pronouns,
|
pronouns: this.pronouns,
|
||||||
badge_ids: this.badge_ids,
|
badge_ids: this.badge_ids,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
clone(): User{
|
clone(): User {
|
||||||
const json=this.tojson();
|
const json = this.tojson();
|
||||||
json.id+="#clone";
|
json.id += "#clone";
|
||||||
return new User(
|
return new User(json, this.owner);
|
||||||
json,
|
|
||||||
this.owner
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPresence(presence: presencejson | undefined): void{
|
public getPresence(presence: presencejson | undefined): void {
|
||||||
if(presence){
|
if (presence) {
|
||||||
this.setstatus(presence.status);
|
this.setstatus(presence.status);
|
||||||
}else{
|
} else {
|
||||||
this.setstatus("offline");
|
this.setstatus("offline");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get online(){
|
get online() {
|
||||||
return (this.status)&&(this.status!="offline");
|
return this.status && this.status != "offline";
|
||||||
}
|
}
|
||||||
setstatus(status: string): void{
|
setstatus(status: string): void {
|
||||||
this.status = status;
|
this.status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
getStatus(): string{
|
getStatus(): string {
|
||||||
return this.status || "offline";
|
return this.status || "offline";
|
||||||
}
|
}
|
||||||
|
|
||||||
static contextmenu = new Contextmenu<User, Member | undefined>("User Menu");
|
static contextmenu = new Contextmenu<User, Member | undefined>("User Menu");
|
||||||
async opendm(){
|
async opendm() {
|
||||||
for(const dm of (this.localuser.guildids.get("@me") as Direct).channels){
|
for (const dm of (this.localuser.guildids.get("@me") as Direct).channels) {
|
||||||
if(dm.user.id===this.id){
|
if (dm.user.id === this.id) {
|
||||||
this.localuser.goToChannel(dm.id);
|
this.localuser.goToChannel(dm.id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await fetch(this.info.api + "/users/@me/channels", {
|
await fetch(this.info.api + "/users/@me/channels", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({ recipients: [this.id] }),
|
body: JSON.stringify({recipients: [this.id]}),
|
||||||
headers: this.localuser.headers,
|
headers: this.localuser.headers,
|
||||||
})
|
})
|
||||||
.then(res=>res.json())
|
.then((res) => res.json())
|
||||||
.then(json=>{
|
.then((json) => {
|
||||||
this.localuser.goToChannel(json.id);
|
this.localuser.goToChannel(json.id);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
async changeRelationship(type:0|1|2|3|4|5){
|
async changeRelationship(type: 0 | 1 | 2 | 3 | 4 | 5) {
|
||||||
if(type!==0){
|
if (type !== 0) {
|
||||||
await fetch(`${this.info.api}/users/@me/relationships/${this.id}`, {
|
await fetch(`${this.info.api}/users/@me/relationships/${this.id}`, {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: this.owner.headers,
|
headers: this.owner.headers,
|
||||||
|
@ -149,197 +154,213 @@ class User extends SnowFlake{
|
||||||
type,
|
type,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}else{
|
} else {
|
||||||
await fetch(`${this.info.api}/users/@me/relationships/${this.id}`, {
|
await fetch(`${this.info.api}/users/@me/relationships/${this.id}`, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: this.owner.headers
|
headers: this.owner.headers,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.relationshipType=type;
|
this.relationshipType = type;
|
||||||
}
|
}
|
||||||
static setUpContextMenu(): void{
|
static setUpContextMenu(): void {
|
||||||
this.contextmenu.addbutton(()=>I18n.getTranslation("user.copyId"), function(this: User){
|
|
||||||
navigator.clipboard.writeText(this.id);
|
|
||||||
});
|
|
||||||
this.contextmenu.addbutton(()=>I18n.getTranslation("user.message"), function(this: User){
|
|
||||||
this.opendm();
|
|
||||||
});
|
|
||||||
this.contextmenu.addbutton(
|
this.contextmenu.addbutton(
|
||||||
()=>I18n.getTranslation("user.block"),
|
() => I18n.getTranslation("user.copyId"),
|
||||||
function(this: User){
|
function (this: User) {
|
||||||
|
navigator.clipboard.writeText(this.id);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
this.contextmenu.addbutton(
|
||||||
|
() => I18n.getTranslation("user.message"),
|
||||||
|
function (this: User) {
|
||||||
|
this.opendm();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
this.contextmenu.addbutton(
|
||||||
|
() => I18n.getTranslation("user.block"),
|
||||||
|
function (this: User) {
|
||||||
this.block();
|
this.block();
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
function(){
|
function () {
|
||||||
return this.relationshipType !== 2;
|
return this.relationshipType !== 2;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
this.contextmenu.addbutton(
|
this.contextmenu.addbutton(
|
||||||
()=>I18n.getTranslation("user.unblock"),
|
() => I18n.getTranslation("user.unblock"),
|
||||||
function(this: User){
|
function (this: User) {
|
||||||
this.unblock();
|
this.unblock();
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
function(){
|
function () {
|
||||||
return this.relationshipType === 2;
|
return this.relationshipType === 2;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
this.contextmenu.addbutton(()=>I18n.getTranslation("user.friendReq"), function(this: User){
|
|
||||||
this.changeRelationship(1);
|
|
||||||
},null,function(){
|
|
||||||
return this.relationshipType===0||this.relationshipType===3;
|
|
||||||
});
|
|
||||||
this.contextmenu.addbutton(()=>I18n.getTranslation("friends.removeFriend"), function(this: User){
|
|
||||||
this.changeRelationship(0);
|
|
||||||
},null,function(){
|
|
||||||
return this.relationshipType===1;
|
|
||||||
});
|
|
||||||
this.contextmenu.addbutton(
|
this.contextmenu.addbutton(
|
||||||
()=>I18n.getTranslation("user.kick"),
|
() => I18n.getTranslation("user.friendReq"),
|
||||||
function(this: User, member: Member | undefined){
|
function (this: User) {
|
||||||
|
this.changeRelationship(1);
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
function () {
|
||||||
|
return this.relationshipType === 0 || this.relationshipType === 3;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
this.contextmenu.addbutton(
|
||||||
|
() => I18n.getTranslation("friends.removeFriend"),
|
||||||
|
function (this: User) {
|
||||||
|
this.changeRelationship(0);
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
function () {
|
||||||
|
return this.relationshipType === 1;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
this.contextmenu.addbutton(
|
||||||
|
() => I18n.getTranslation("user.kick"),
|
||||||
|
function (this: User, member: Member | undefined) {
|
||||||
member?.kick();
|
member?.kick();
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
member=>{
|
(member) => {
|
||||||
if(!member)return false;
|
if (!member) return false;
|
||||||
const us = member.guild.member;
|
const us = member.guild.member;
|
||||||
if(member.id === us.id){
|
if (member.id === us.id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(member.id === member.guild.properties.owner_id){
|
if (member.id === member.guild.properties.owner_id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return us.hasPermission("KICK_MEMBERS") || false;
|
return us.hasPermission("KICK_MEMBERS") || false;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
this.contextmenu.addbutton(
|
this.contextmenu.addbutton(
|
||||||
()=>I18n.getTranslation("user.editServerProfile"),
|
() => I18n.getTranslation("user.editServerProfile"),
|
||||||
function(this: User, member: Member | undefined){
|
function (this: User, member: Member | undefined) {
|
||||||
if(!member) return;
|
if (!member) return;
|
||||||
member.showEditProfile();
|
member.showEditProfile();
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
function(member){
|
function (member) {
|
||||||
return member?.id===this.localuser.user.id;
|
return member?.id === this.localuser.user.id;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
this.contextmenu.addbutton(
|
this.contextmenu.addbutton(
|
||||||
()=>I18n.getTranslation("user.ban"),
|
() => I18n.getTranslation("user.ban"),
|
||||||
function(this: User, member: Member | undefined){
|
function (this: User, member: Member | undefined) {
|
||||||
member?.ban();
|
member?.ban();
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
member=>{
|
(member) => {
|
||||||
if(!member)return false;
|
if (!member) return false;
|
||||||
const us = member.guild.member;
|
const us = member.guild.member;
|
||||||
if(member.id === us.id){
|
if (member.id === us.id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(member.id === member.guild.properties.owner_id){
|
if (member.id === member.guild.properties.owner_id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return us.hasPermission("BAN_MEMBERS") || false;
|
return us.hasPermission("BAN_MEMBERS") || false;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
this.contextmenu.addbutton(
|
this.contextmenu.addbutton(
|
||||||
()=>I18n.getTranslation("user.addRole"),
|
() => I18n.getTranslation("user.addRole"),
|
||||||
async function(this: User, member: Member | undefined,e){
|
async function (this: User, member: Member | undefined, e) {
|
||||||
if(member){
|
if (member) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const roles:[Role,string[]][]=[];
|
const roles: [Role, string[]][] = [];
|
||||||
for(const role of member.guild.roles){
|
for (const role of member.guild.roles) {
|
||||||
if(!role.canManage()||member.roles.indexOf(role)!==-1){
|
if (!role.canManage() || member.roles.indexOf(role) !== -1) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
roles.push([role,[role.name]]);
|
roles.push([role, [role.name]]);
|
||||||
}
|
}
|
||||||
const search=new Search(roles);
|
const search = new Search(roles);
|
||||||
const result=await search.find(e.x,e.y);
|
const result = await search.find(e.x, e.y);
|
||||||
if(!result) return;
|
if (!result) return;
|
||||||
member.addRole(result);
|
member.addRole(result);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
member=>{
|
(member) => {
|
||||||
if(!member)return false;
|
if (!member) return false;
|
||||||
const us = member.guild.member;
|
const us = member.guild.member;
|
||||||
console.log(us.hasPermission("MANAGE_ROLES"))
|
console.log(us.hasPermission("MANAGE_ROLES"));
|
||||||
return us.hasPermission("MANAGE_ROLES") || false;
|
return us.hasPermission("MANAGE_ROLES") || false;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
this.contextmenu.addbutton(
|
this.contextmenu.addbutton(
|
||||||
()=>I18n.getTranslation("user.removeRole"),
|
() => I18n.getTranslation("user.removeRole"),
|
||||||
async function(this: User, member: Member | undefined,e){
|
async function (this: User, member: Member | undefined, e) {
|
||||||
if(member){
|
if (member) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const roles:[Role,string[]][]=[];
|
const roles: [Role, string[]][] = [];
|
||||||
for(const role of member.roles){
|
for (const role of member.roles) {
|
||||||
if(!role.canManage()){
|
if (!role.canManage()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
roles.push([role,[role.name]]);
|
roles.push([role, [role.name]]);
|
||||||
}
|
}
|
||||||
const search=new Search(roles);
|
const search = new Search(roles);
|
||||||
const result=await search.find(e.x,e.y);
|
const result = await search.find(e.x, e.y);
|
||||||
if(!result) return;
|
if (!result) return;
|
||||||
member.removeRole(result);
|
member.removeRole(result);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
member=>{
|
(member) => {
|
||||||
if(!member)return false;
|
if (!member) return false;
|
||||||
const us = member.guild.member;
|
const us = member.guild.member;
|
||||||
console.log(us.hasPermission("MANAGE_ROLES"))
|
console.log(us.hasPermission("MANAGE_ROLES"));
|
||||||
return us.hasPermission("MANAGE_ROLES") || false;
|
return us.hasPermission("MANAGE_ROLES") || false;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static checkuser(user: User | userjson, owner: Localuser): User{
|
static checkuser(user: User | userjson, owner: Localuser): User {
|
||||||
if(owner.userMap.has(user.id)){
|
if (owner.userMap.has(user.id)) {
|
||||||
return owner.userMap.get(user.id) as User;
|
return owner.userMap.get(user.id) as User;
|
||||||
}else{
|
} else {
|
||||||
const tempuser = new User(user as userjson, owner, true);
|
const tempuser = new User(user as userjson, owner, true);
|
||||||
owner.userMap.set(user.id, tempuser);
|
owner.userMap.set(user.id, tempuser);
|
||||||
return tempuser;
|
return tempuser;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get info(){
|
get info() {
|
||||||
return this.owner.info;
|
return this.owner.info;
|
||||||
}
|
}
|
||||||
|
|
||||||
get localuser(){
|
get localuser() {
|
||||||
return this.owner;
|
return this.owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
get name(){
|
get name() {
|
||||||
return this.username;
|
return this.username;
|
||||||
}
|
}
|
||||||
|
|
||||||
async resolvemember(guild: Guild): Promise<Member | undefined>{
|
async resolvemember(guild: Guild): Promise<Member | undefined> {
|
||||||
return await Member.resolveMember(this, guild);
|
return await Member.resolveMember(this, guild);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getUserProfile(): Promise<any>{
|
async getUserProfile(): Promise<any> {
|
||||||
return await fetch(
|
return await fetch(
|
||||||
`${this.info.api}/users/${this.id.replace(
|
`${this.info.api}/users/${this.id.replace(
|
||||||
"#clone",
|
"#clone",
|
||||||
""
|
"",
|
||||||
)}/profile?with_mutual_guilds=true&with_mutual_friends=true`,
|
)}/profile?with_mutual_guilds=true&with_mutual_friends=true`,
|
||||||
{
|
{
|
||||||
headers: this.localuser.headers,
|
headers: this.localuser.headers,
|
||||||
}
|
},
|
||||||
).then(res=>res.json());
|
).then((res) => res.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBadge(id: string): Promise<any>{
|
async getBadge(id: string): Promise<any> {
|
||||||
if(this.localuser.badges.has(id)){
|
if (this.localuser.badges.has(id)) {
|
||||||
return this.localuser.badges.get(id);
|
return this.localuser.badges.get(id);
|
||||||
}else{
|
} else {
|
||||||
if(this.resolving){
|
if (this.resolving) {
|
||||||
await this.resolving;
|
await this.resolving;
|
||||||
return this.localuser.badges.get(id);
|
return this.localuser.badges.get(id);
|
||||||
}
|
}
|
||||||
|
@ -348,47 +369,45 @@ class User extends SnowFlake{
|
||||||
this.resolving = prom;
|
this.resolving = prom;
|
||||||
const badges = prom.badges;
|
const badges = prom.badges;
|
||||||
this.resolving = false;
|
this.resolving = false;
|
||||||
for(const badge of badges){
|
for (const badge of badges) {
|
||||||
this.localuser.badges.set(badge.id, badge);
|
this.localuser.badges.set(badge.id, badge);
|
||||||
}
|
}
|
||||||
return this.localuser.badges.get(id);
|
return this.localuser.badges.get(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildpfp(guild:Guild|void|Member|null): HTMLImageElement{
|
buildpfp(guild: Guild | void | Member | null): HTMLImageElement {
|
||||||
const pfp = document.createElement("img");
|
const pfp = document.createElement("img");
|
||||||
pfp.loading = "lazy";
|
pfp.loading = "lazy";
|
||||||
pfp.src = this.getpfpsrc();
|
pfp.src = this.getpfpsrc();
|
||||||
pfp.classList.add("pfp");
|
pfp.classList.add("pfp");
|
||||||
pfp.classList.add("userid:" + this.id);
|
pfp.classList.add("userid:" + this.id);
|
||||||
if(guild){
|
if (guild) {
|
||||||
(async()=>{
|
(async () => {
|
||||||
if(guild instanceof Guild){
|
if (guild instanceof Guild) {
|
||||||
const memb= await Member.resolveMember(this,guild)
|
const memb = await Member.resolveMember(this, guild);
|
||||||
if(!memb) return;
|
if (!memb) return;
|
||||||
pfp.src = memb.getpfpsrc();
|
pfp.src = memb.getpfpsrc();
|
||||||
}else{
|
} else {
|
||||||
pfp.src = guild.getpfpsrc();
|
pfp.src = guild.getpfpsrc();
|
||||||
}
|
}
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
}
|
}
|
||||||
return pfp;
|
return pfp;
|
||||||
}
|
}
|
||||||
|
|
||||||
async buildstatuspfp(guild:Guild|void|Member|null): Promise<HTMLDivElement>{
|
async buildstatuspfp(guild: Guild | void | Member | null): Promise<HTMLDivElement> {
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
div.classList.add("pfpDiv")
|
div.classList.add("pfpDiv");
|
||||||
const pfp = this.buildpfp(guild);
|
const pfp = this.buildpfp(guild);
|
||||||
div.append(pfp);
|
div.append(pfp);
|
||||||
const status = document.createElement("div");
|
const status = document.createElement("div");
|
||||||
status.classList.add("statusDiv");
|
status.classList.add("statusDiv");
|
||||||
switch(await this.getStatus()){
|
switch (await this.getStatus()) {
|
||||||
case"offline":
|
case "offline":
|
||||||
status.classList.add("offlinestatus");
|
status.classList.add("offlinestatus");
|
||||||
break;
|
break;
|
||||||
case"online":
|
case "online":
|
||||||
default:
|
default:
|
||||||
status.classList.add("onlinestatus");
|
status.classList.add("onlinestatus");
|
||||||
break;
|
break;
|
||||||
|
@ -397,77 +416,74 @@ class User extends SnowFlake{
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
|
|
||||||
userupdate(json: userjson): void{
|
userupdate(json: userjson): void {
|
||||||
if(json.avatar !== this.avatar){
|
if (json.avatar !== this.avatar) {
|
||||||
this.changepfp(json.avatar);
|
this.changepfp(json.avatar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bind(html: HTMLElement, guild: Guild | null = null, error = true): void{
|
bind(html: HTMLElement, guild: Guild | null = null, error = true): void {
|
||||||
if(guild && guild.id !== "@me"){
|
if (guild && guild.id !== "@me") {
|
||||||
Member.resolveMember(this, guild)
|
Member.resolveMember(this, guild)
|
||||||
.then(member=>{
|
.then((member) => {
|
||||||
User.contextmenu.bindContextmenu(html, this, member);
|
User.contextmenu.bindContextmenu(html, this, member);
|
||||||
if(member === undefined && error){
|
if (member === undefined && error) {
|
||||||
const errorSpan = document.createElement("span");
|
const errorSpan = document.createElement("span");
|
||||||
errorSpan.textContent = "!";
|
errorSpan.textContent = "!";
|
||||||
errorSpan.classList.add("membererror");
|
errorSpan.classList.add("membererror");
|
||||||
html.after(errorSpan);
|
html.after(errorSpan);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(member){
|
if (member) {
|
||||||
member.bind(html);
|
member.bind(html);
|
||||||
}else{
|
} else {
|
||||||
User.contextmenu.bindContextmenu(html, this, undefined);
|
User.contextmenu.bindContextmenu(html, this, undefined);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(err=>{
|
.catch((err) => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
});
|
});
|
||||||
}else{
|
} else {
|
||||||
User.contextmenu.bindContextmenu(html, this, undefined);
|
User.contextmenu.bindContextmenu(html, this, undefined);
|
||||||
}
|
}
|
||||||
if(guild){
|
if (guild) {
|
||||||
this.profileclick(html, guild);
|
this.profileclick(html, guild);
|
||||||
}else{
|
} else {
|
||||||
this.profileclick(html);
|
this.profileclick(html);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async resolve(id: string, localuser: Localuser): Promise<User>{
|
static async resolve(id: string, localuser: Localuser): Promise<User> {
|
||||||
const json = await fetch(
|
const json = await fetch(localuser.info.api.toString() + "/users/" + id + "/profile", {
|
||||||
localuser.info.api.toString() + "/users/" + id + "/profile",
|
headers: localuser.headers,
|
||||||
{ headers: localuser.headers }
|
}).then((res) => res.json());
|
||||||
).then(res=>res.json());
|
|
||||||
return new User(json.user, localuser);
|
return new User(json.user, localuser);
|
||||||
}
|
}
|
||||||
|
|
||||||
changepfp(update: string | null): void{
|
changepfp(update: string | null): void {
|
||||||
this.avatar = update;
|
this.avatar = update;
|
||||||
this.hypotheticalpfp = false;
|
this.hypotheticalpfp = false;
|
||||||
const src = this.getpfpsrc();
|
const src = this.getpfpsrc();
|
||||||
Array.from(document.getElementsByClassName("userid:" + this.id)).forEach(
|
Array.from(document.getElementsByClassName("userid:" + this.id)).forEach((element) => {
|
||||||
element=>{
|
|
||||||
(element as HTMLImageElement).src = src;
|
(element as HTMLImageElement).src = src;
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async block(){
|
async block() {
|
||||||
await this.changeRelationship(2);
|
await this.changeRelationship(2);
|
||||||
const channel = this.localuser.channelfocus;
|
const channel = this.localuser.channelfocus;
|
||||||
if(channel){
|
if (channel) {
|
||||||
for(const message of channel.messages){
|
for (const message of channel.messages) {
|
||||||
message[1].generateMessage();
|
message[1].generateMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async unblock(){
|
async unblock() {
|
||||||
await this.changeRelationship(0);
|
await this.changeRelationship(0);
|
||||||
const channel = this.localuser.channelfocus;
|
const channel = this.localuser.channelfocus;
|
||||||
if(channel){
|
if (channel) {
|
||||||
for(const message of channel.messages){
|
for (const message of channel.messages) {
|
||||||
message[1].generateMessage();
|
message[1].generateMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -475,81 +491,79 @@ class User extends SnowFlake{
|
||||||
/**
|
/**
|
||||||
* @param guild this is an optional thing that'll get the src of the member if it exists, otherwise ignores it, this is meant to be fast, not accurate
|
* @param guild this is an optional thing that'll get the src of the member if it exists, otherwise ignores it, this is meant to be fast, not accurate
|
||||||
*/
|
*/
|
||||||
getpfpsrc(guild:Guild|void): string{
|
getpfpsrc(guild: Guild | void): string {
|
||||||
if(this.hypotheticalpfp && this.avatar){
|
if (this.hypotheticalpfp && this.avatar) {
|
||||||
return this.avatar;
|
return this.avatar;
|
||||||
}
|
}
|
||||||
if(guild){
|
if (guild) {
|
||||||
const member=this.members.get(guild)
|
const member = this.members.get(guild);
|
||||||
if(member instanceof Member){
|
if (member instanceof Member) {
|
||||||
return member.getpfpsrc();
|
return member.getpfpsrc();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(this.avatar !== null){
|
if (this.avatar !== null) {
|
||||||
return`${this.info.cdn}/avatars/${this.id.replace("#clone", "")}/${
|
return `${this.info.cdn}/avatars/${this.id.replace("#clone", "")}/${this.avatar}.png`;
|
||||||
this.avatar
|
} else {
|
||||||
}.png`;
|
|
||||||
}else{
|
|
||||||
const int = Number((BigInt(this.id.replace("#clone", "")) >> 22n) % 6n);
|
const int = Number((BigInt(this.id.replace("#clone", "")) >> 22n) % 6n);
|
||||||
return`${this.info.cdn}/embed/avatars/${int}.png`;
|
return `${this.info.cdn}/embed/avatars/${int}.png`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async buildprofile(
|
async buildprofile(
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
guild: Guild | null | Member = null
|
guild: Guild | null | Member = null,
|
||||||
): Promise<HTMLDivElement>{
|
): Promise<HTMLDivElement> {
|
||||||
if(Contextmenu.currentmenu != ""){
|
if (Contextmenu.currentmenu != "") {
|
||||||
Contextmenu.currentmenu.remove();
|
Contextmenu.currentmenu.remove();
|
||||||
}
|
}
|
||||||
const membres=(async ()=>{
|
const membres = (async () => {
|
||||||
if(!guild) return;
|
if (!guild) return;
|
||||||
let member:Member|undefined;
|
let member: Member | undefined;
|
||||||
if(guild instanceof Guild){
|
if (guild instanceof Guild) {
|
||||||
member=await Member.resolveMember(this,guild)
|
member = await Member.resolveMember(this, guild);
|
||||||
}else{
|
} else {
|
||||||
member=guild;
|
member = guild;
|
||||||
}
|
}
|
||||||
return member;
|
return member;
|
||||||
})()
|
})();
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
|
|
||||||
if(this.accent_color){
|
if (this.accent_color) {
|
||||||
div.style.setProperty(
|
div.style.setProperty(
|
||||||
"--accent_color",
|
"--accent_color",
|
||||||
`#${this.accent_color.toString(16).padStart(6, "0")}`
|
`#${this.accent_color.toString(16).padStart(6, "0")}`,
|
||||||
);
|
);
|
||||||
}else{
|
} else {
|
||||||
div.style.setProperty("--accent_color", "transparent");
|
div.style.setProperty("--accent_color", "transparent");
|
||||||
}
|
}
|
||||||
const banner=this.getBanner(guild);
|
const banner = this.getBanner(guild);
|
||||||
div.append(banner);
|
div.append(banner);
|
||||||
membres.then(member=>{
|
membres.then((member) => {
|
||||||
if(!member) return;
|
if (!member) return;
|
||||||
if(member.accent_color&&member.accent_color!==0){
|
if (member.accent_color && member.accent_color !== 0) {
|
||||||
div.style.setProperty(
|
div.style.setProperty(
|
||||||
"--accent_color",
|
"--accent_color",
|
||||||
`#${member.accent_color.toString(16).padStart(6, "0")}`
|
`#${member.accent_color.toString(16).padStart(6, "0")}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
if(x !== -1){
|
if (x !== -1) {
|
||||||
div.style.left = `${x}px`;
|
div.style.left = `${x}px`;
|
||||||
div.style.top = `${y}px`;
|
div.style.top = `${y}px`;
|
||||||
div.classList.add("profile", "flexttb");
|
div.classList.add("profile", "flexttb");
|
||||||
}else{
|
} else {
|
||||||
this.setstatus("online");
|
this.setstatus("online");
|
||||||
div.classList.add("hypoprofile", "profile", "flexttb");
|
div.classList.add("hypoprofile", "profile", "flexttb");
|
||||||
}
|
}
|
||||||
const badgediv = document.createElement("div");
|
const badgediv = document.createElement("div");
|
||||||
badgediv.classList.add("badges");
|
badgediv.classList.add("badges");
|
||||||
(async ()=>{
|
(async () => {
|
||||||
if(!this.badge_ids)return;
|
if (!this.badge_ids) return;
|
||||||
for(const id of this.badge_ids){
|
for (const id of this.badge_ids) {
|
||||||
const badgejson = await this.getBadge(id);
|
const badgejson = await this.getBadge(id);
|
||||||
if(badgejson){
|
if (badgejson) {
|
||||||
const badge = document.createElement(badgejson.link ? "a" : "div");
|
const badge = document.createElement(badgejson.link ? "a" : "div");
|
||||||
badge.classList.add("badge");
|
badge.classList.add("badge");
|
||||||
const img = document.createElement("img");
|
const img = document.createElement("img");
|
||||||
|
@ -558,7 +572,7 @@ class User extends SnowFlake{
|
||||||
const span = document.createElement("span");
|
const span = document.createElement("span");
|
||||||
span.textContent = badgejson.description;
|
span.textContent = badgejson.description;
|
||||||
badge.append(span);
|
badge.append(span);
|
||||||
if(badge instanceof HTMLAnchorElement){
|
if (badge instanceof HTMLAnchorElement) {
|
||||||
badge.href = badgejson.link;
|
badge.href = badgejson.link;
|
||||||
}
|
}
|
||||||
badgediv.append(badge);
|
badgediv.append(badge);
|
||||||
|
@ -568,7 +582,7 @@ class User extends SnowFlake{
|
||||||
const pfp = await this.buildstatuspfp(guild);
|
const pfp = await this.buildstatuspfp(guild);
|
||||||
div.appendChild(pfp);
|
div.appendChild(pfp);
|
||||||
const userbody = document.createElement("div");
|
const userbody = document.createElement("div");
|
||||||
userbody.classList.add("flexttb","infosection");
|
userbody.classList.add("flexttb", "infosection");
|
||||||
div.appendChild(userbody);
|
div.appendChild(userbody);
|
||||||
const usernamehtml = document.createElement("h2");
|
const usernamehtml = document.createElement("h2");
|
||||||
usernamehtml.textContent = this.username;
|
usernamehtml.textContent = this.username;
|
||||||
|
@ -580,14 +594,14 @@ class User extends SnowFlake{
|
||||||
userbody.appendChild(discrimatorhtml);
|
userbody.appendChild(discrimatorhtml);
|
||||||
|
|
||||||
const pronounshtml = document.createElement("p");
|
const pronounshtml = document.createElement("p");
|
||||||
pronounshtml.textContent = this.pronouns||"";
|
pronounshtml.textContent = this.pronouns || "";
|
||||||
pronounshtml.classList.add("pronouns");
|
pronounshtml.classList.add("pronouns");
|
||||||
userbody.appendChild(pronounshtml);
|
userbody.appendChild(pronounshtml);
|
||||||
|
|
||||||
membres.then(member=>{
|
membres.then((member) => {
|
||||||
if(!member) return;
|
if (!member) return;
|
||||||
if(member.pronouns&&member.pronouns!==""){
|
if (member.pronouns && member.pronouns !== "") {
|
||||||
pronounshtml.textContent=member.pronouns;
|
pronounshtml.textContent = member.pronouns;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -596,31 +610,28 @@ class User extends SnowFlake{
|
||||||
const biohtml = this.bio.makeHTML();
|
const biohtml = this.bio.makeHTML();
|
||||||
userbody.appendChild(biohtml);
|
userbody.appendChild(biohtml);
|
||||||
|
|
||||||
membres.then(member=>{
|
membres.then((member) => {
|
||||||
if(!member)return;
|
if (!member) return;
|
||||||
if(member.bio&&member.bio!==""){
|
if (member.bio && member.bio !== "") {
|
||||||
//TODO make markdown take Guild
|
//TODO make markdown take Guild
|
||||||
userbody.insertBefore(new MarkDown(member.bio,this.localuser).makeHTML(),biohtml);
|
userbody.insertBefore(new MarkDown(member.bio, this.localuser).makeHTML(), biohtml);
|
||||||
biohtml.remove();
|
biohtml.remove();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if(guild){
|
if (guild) {
|
||||||
membres.then(member=>{
|
membres.then((member) => {
|
||||||
if(!member)return;
|
if (!member) return;
|
||||||
usernamehtml.textContent=member.name;
|
usernamehtml.textContent = member.name;
|
||||||
const roles = document.createElement("div");
|
const roles = document.createElement("div");
|
||||||
roles.classList.add("flexltr","rolesbox");
|
roles.classList.add("flexltr", "rolesbox");
|
||||||
for(const role of member.roles){
|
for (const role of member.roles) {
|
||||||
if(role.id===member.guild.id) continue;
|
if (role.id === member.guild.id) continue;
|
||||||
const roleDiv = document.createElement("div");
|
const roleDiv = document.createElement("div");
|
||||||
roleDiv.classList.add("rolediv");
|
roleDiv.classList.add("rolediv");
|
||||||
const color = document.createElement("div");
|
const color = document.createElement("div");
|
||||||
roleDiv.append(color);
|
roleDiv.append(color);
|
||||||
color.style.setProperty(
|
color.style.setProperty("--role-color", `#${role.color.toString(16).padStart(6, "0")}`);
|
||||||
"--role-color",
|
|
||||||
`#${role.color.toString(16).padStart(6, "0")}`
|
|
||||||
);
|
|
||||||
color.classList.add("colorrolediv");
|
color.classList.add("colorrolediv");
|
||||||
const span = document.createElement("span");
|
const span = document.createElement("span");
|
||||||
roleDiv.append(span);
|
roleDiv.append(span);
|
||||||
|
@ -630,57 +641,55 @@ class User extends SnowFlake{
|
||||||
userbody.append(roles);
|
userbody.append(roles);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if(x !== -1){
|
if (x !== -1) {
|
||||||
Contextmenu.currentmenu = div;
|
Contextmenu.currentmenu = div;
|
||||||
document.body.appendChild(div);
|
document.body.appendChild(div);
|
||||||
Contextmenu.keepOnScreen(div);
|
Contextmenu.keepOnScreen(div);
|
||||||
}
|
}
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
getBanner(guild:Guild|null|Member):HTMLImageElement{
|
getBanner(guild: Guild | null | Member): HTMLImageElement {
|
||||||
const banner = document.createElement("img");
|
const banner = document.createElement("img");
|
||||||
|
|
||||||
const bsrc=this.getBannerUrl();
|
const bsrc = this.getBannerUrl();
|
||||||
if(bsrc){
|
if (bsrc) {
|
||||||
banner.src = bsrc;
|
banner.src = bsrc;
|
||||||
banner.classList.add("banner");
|
banner.classList.add("banner");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(guild){
|
if (guild) {
|
||||||
if(guild instanceof Member){
|
if (guild instanceof Member) {
|
||||||
const bsrc=guild.getBannerUrl();
|
const bsrc = guild.getBannerUrl();
|
||||||
if(bsrc){
|
if (bsrc) {
|
||||||
banner.src = bsrc;
|
banner.src = bsrc;
|
||||||
banner.classList.add("banner");
|
banner.classList.add("banner");
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
Member.resolveMember(this,guild).then(memb=>{
|
Member.resolveMember(this, guild).then((memb) => {
|
||||||
if(!memb) return;
|
if (!memb) return;
|
||||||
const bsrc=memb.getBannerUrl();
|
const bsrc = memb.getBannerUrl();
|
||||||
if(bsrc){
|
if (bsrc) {
|
||||||
banner.src = bsrc;
|
banner.src = bsrc;
|
||||||
banner.classList.add("banner");
|
banner.classList.add("banner");
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return banner
|
return banner;
|
||||||
}
|
}
|
||||||
getBannerUrl():string|undefined{
|
getBannerUrl(): string | undefined {
|
||||||
if(this.banner){
|
if (this.banner) {
|
||||||
if(!this.hypotheticalbanner){
|
if (!this.hypotheticalbanner) {
|
||||||
return `${this.info.cdn}/avatars/${this.id.replace("#clone", "")}/${
|
return `${this.info.cdn}/avatars/${this.id.replace("#clone", "")}/${this.banner}.png`;
|
||||||
this.banner
|
} else {
|
||||||
}.png`;
|
|
||||||
}else{
|
|
||||||
return this.banner;
|
return this.banner;
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
profileclick(obj: HTMLElement, guild?: Guild): void{
|
profileclick(obj: HTMLElement, guild?: Guild): void {
|
||||||
obj.onclick = (e: MouseEvent)=>{
|
obj.onclick = (e: MouseEvent) => {
|
||||||
this.buildprofile(e.clientX, e.clientY, guild);
|
this.buildprofile(e.clientX, e.clientY, guild);
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
};
|
};
|
||||||
|
@ -688,4 +697,4 @@ class User extends SnowFlake{
|
||||||
}
|
}
|
||||||
|
|
||||||
User.setUpContextMenu();
|
User.setUpContextMenu();
|
||||||
export{ User };
|
export {User};
|
||||||
|
|
|
@ -1,33 +1,33 @@
|
||||||
class BinRead{
|
class BinRead {
|
||||||
private i = 0;
|
private i = 0;
|
||||||
private view:DataView;
|
private view: DataView;
|
||||||
constructor(buffer:ArrayBuffer){
|
constructor(buffer: ArrayBuffer) {
|
||||||
this.view=new DataView(buffer, 0)
|
this.view = new DataView(buffer, 0);
|
||||||
}
|
}
|
||||||
read16(){
|
read16() {
|
||||||
const int = this.view.getUint16(this.i);
|
const int = this.view.getUint16(this.i);
|
||||||
this.i += 2;
|
this.i += 2;
|
||||||
return int;
|
return int;
|
||||||
}
|
}
|
||||||
read8(){
|
read8() {
|
||||||
const int = this.view.getUint8(this.i);
|
const int = this.view.getUint8(this.i);
|
||||||
this.i += 1;
|
this.i += 1;
|
||||||
return int;
|
return int;
|
||||||
}
|
}
|
||||||
readString8(){
|
readString8() {
|
||||||
return this.readStringNo(this.read8());
|
return this.readStringNo(this.read8());
|
||||||
}
|
}
|
||||||
readString16(){
|
readString16() {
|
||||||
return this.readStringNo(this.read16());
|
return this.readStringNo(this.read16());
|
||||||
}
|
}
|
||||||
readFloat32(){
|
readFloat32() {
|
||||||
const float = this.view.getFloat32(this.i);
|
const float = this.view.getFloat32(this.i);
|
||||||
this.i += 4;
|
this.i += 4;
|
||||||
return float;
|
return float;
|
||||||
}
|
}
|
||||||
readStringNo(length: number){
|
readStringNo(length: number) {
|
||||||
const array = new Uint8Array(length);
|
const array = new Uint8Array(length);
|
||||||
for(let i = 0; i < length; i++){
|
for (let i = 0; i < length; i++) {
|
||||||
array[i] = this.read8();
|
array[i] = this.read8();
|
||||||
}
|
}
|
||||||
//console.log(array);
|
//console.log(array);
|
||||||
|
@ -35,54 +35,54 @@ class BinRead{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BinWrite{
|
class BinWrite {
|
||||||
private view: DataView;
|
private view: DataView;
|
||||||
private buffer:ArrayBuffer;
|
private buffer: ArrayBuffer;
|
||||||
private i=0;
|
private i = 0;
|
||||||
constructor(maxSize:number=2**26){
|
constructor(maxSize: number = 2 ** 26) {
|
||||||
this.buffer=new ArrayBuffer(maxSize);
|
this.buffer = new ArrayBuffer(maxSize);
|
||||||
this.view=new DataView(this.buffer, 0);
|
this.view = new DataView(this.buffer, 0);
|
||||||
}
|
}
|
||||||
write32Float(numb:number){
|
write32Float(numb: number) {
|
||||||
this.view.setFloat32(this.i,numb);
|
this.view.setFloat32(this.i, numb);
|
||||||
this.i+=4;
|
this.i += 4;
|
||||||
}
|
}
|
||||||
write16(numb:number){
|
write16(numb: number) {
|
||||||
this.view.setUint16(this.i,numb);
|
this.view.setUint16(this.i, numb);
|
||||||
this.i+=2;
|
this.i += 2;
|
||||||
}
|
}
|
||||||
write8(numb:number){
|
write8(numb: number) {
|
||||||
this.view.setUint8(this.i,numb);
|
this.view.setUint8(this.i, numb);
|
||||||
this.i+=1;
|
this.i += 1;
|
||||||
}
|
}
|
||||||
writeString8(str:string){
|
writeString8(str: string) {
|
||||||
const encode=new TextEncoder().encode(str);
|
const encode = new TextEncoder().encode(str);
|
||||||
this.write8(encode.length);
|
this.write8(encode.length);
|
||||||
for(const thing of encode){
|
for (const thing of encode) {
|
||||||
this.write8(thing);
|
this.write8(thing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
writeString16(str:string){
|
writeString16(str: string) {
|
||||||
const encode=new TextEncoder().encode(str);
|
const encode = new TextEncoder().encode(str);
|
||||||
this.write16(encode.length);
|
this.write16(encode.length);
|
||||||
for(const thing of encode){
|
for (const thing of encode) {
|
||||||
this.write8(thing);
|
this.write8(thing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
writeStringNo(str:string){
|
writeStringNo(str: string) {
|
||||||
const encode=new TextEncoder().encode(str);
|
const encode = new TextEncoder().encode(str);
|
||||||
for(const thing of encode){
|
for (const thing of encode) {
|
||||||
this.write8(thing);
|
this.write8(thing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getBuffer(){
|
getBuffer() {
|
||||||
const buf=new ArrayBuffer(this.i);
|
const buf = new ArrayBuffer(this.i);
|
||||||
const ar1=new Uint8Array(buf);
|
const ar1 = new Uint8Array(buf);
|
||||||
const ar2=new Uint8Array(this.buffer);
|
const ar2 = new Uint8Array(this.buffer);
|
||||||
for(let i in ar1){
|
for (let i in ar1) {
|
||||||
ar1[+i]=ar2[+i];
|
ar1[+i] = ar2[+i];
|
||||||
}
|
}
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export {BinRead,BinWrite}
|
export {BinRead, BinWrite};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { I18n } from "../i18n.js";
|
import {I18n} from "../i18n.js";
|
||||||
setTheme();
|
setTheme();
|
||||||
export function setTheme() {
|
export function setTheme() {
|
||||||
let name = localStorage.getItem("theme");
|
let name = localStorage.getItem("theme");
|
||||||
|
@ -31,7 +31,7 @@ export function setDefaults() {
|
||||||
notifications: false,
|
notifications: false,
|
||||||
notisound: "three",
|
notisound: "three",
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
userinfos = getBulkInfo();
|
userinfos = getBulkInfo();
|
||||||
}
|
}
|
||||||
|
@ -41,10 +41,7 @@ export function setDefaults() {
|
||||||
if (userinfos.accent_color === undefined) {
|
if (userinfos.accent_color === undefined) {
|
||||||
userinfos.accent_color = "#3096f7";
|
userinfos.accent_color = "#3096f7";
|
||||||
}
|
}
|
||||||
document.documentElement.style.setProperty(
|
document.documentElement.style.setProperty("--accent-color", userinfos.accent_color);
|
||||||
"--accent-color",
|
|
||||||
userinfos.accent_color
|
|
||||||
);
|
|
||||||
if (userinfos.preferences === undefined) {
|
if (userinfos.preferences === undefined) {
|
||||||
userinfos.preferences = {
|
userinfos.preferences = {
|
||||||
theme: "Dark",
|
theme: "Dark",
|
||||||
|
@ -79,27 +76,17 @@ export class Specialuser {
|
||||||
let apistring = new URL(json.serverurls.api).toString();
|
let apistring = new URL(json.serverurls.api).toString();
|
||||||
apistring = apistring.replace(/\/(v\d+\/?)?$/, "") + "/v9";
|
apistring = apistring.replace(/\/(v\d+\/?)?$/, "") + "/v9";
|
||||||
this.serverurls.api = apistring;
|
this.serverurls.api = apistring;
|
||||||
this.serverurls.cdn = new URL(json.serverurls.cdn)
|
this.serverurls.cdn = new URL(json.serverurls.cdn).toString().replace(/\/$/, "");
|
||||||
.toString()
|
this.serverurls.gateway = new URL(json.serverurls.gateway).toString().replace(/\/$/, "");
|
||||||
.replace(/\/$/, "");
|
this.serverurls.wellknown = new URL(json.serverurls.wellknown).toString().replace(/\/$/, "");
|
||||||
this.serverurls.gateway = new URL(json.serverurls.gateway)
|
this.serverurls.login = new URL(json.serverurls.login).toString().replace(/\/$/, "");
|
||||||
.toString()
|
|
||||||
.replace(/\/$/, "");
|
|
||||||
this.serverurls.wellknown = new URL(json.serverurls.wellknown)
|
|
||||||
.toString()
|
|
||||||
.replace(/\/$/, "");
|
|
||||||
this.serverurls.login = new URL(json.serverurls.login)
|
|
||||||
.toString()
|
|
||||||
.replace(/\/$/, "");
|
|
||||||
this.email = json.email;
|
this.email = json.email;
|
||||||
this.token = json.token;
|
this.token = json.token;
|
||||||
this.loggedin = json.loggedin;
|
this.loggedin = json.loggedin;
|
||||||
this.json = json;
|
this.json = json;
|
||||||
this.json.localuserStore ??= {};
|
this.json.localuserStore ??= {};
|
||||||
if (!this.serverurls || !this.email || !this.token) {
|
if (!this.serverurls || !this.email || !this.token) {
|
||||||
console.error(
|
console.error("There are fundamentally missing pieces of info missing from this user");
|
||||||
"There are fundamentally missing pieces of info missing from this user"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set pfpsrc(e) {
|
set pfpsrc(e) {
|
||||||
|
@ -144,12 +131,9 @@ export class Specialuser {
|
||||||
}
|
}
|
||||||
const mobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
|
const mobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
|
||||||
const iOS = /iPhone|iPad|iPod/i.test(navigator.userAgent);
|
const iOS = /iPhone|iPad|iPod/i.test(navigator.userAgent);
|
||||||
export{
|
export {mobile, iOS};
|
||||||
mobile,
|
|
||||||
iOS,
|
|
||||||
}
|
|
||||||
let instances:
|
let instances:
|
||||||
| {
|
| {
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
descriptionLong?: string;
|
descriptionLong?: string;
|
||||||
|
@ -157,7 +141,7 @@ let instances:
|
||||||
url?: string;
|
url?: string;
|
||||||
display?: boolean;
|
display?: boolean;
|
||||||
online?: boolean;
|
online?: boolean;
|
||||||
uptime: { alltime: number; daytime: number; weektime: number };
|
uptime: {alltime: number; daytime: number; weektime: number};
|
||||||
urls: {
|
urls: {
|
||||||
wellknown: string;
|
wellknown: string;
|
||||||
api: string;
|
api: string;
|
||||||
|
@ -165,14 +149,15 @@ let instances:
|
||||||
gateway: string;
|
gateway: string;
|
||||||
login?: string;
|
login?: string;
|
||||||
};
|
};
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
const datalist = document.getElementById("instances");
|
const datalist = document.getElementById("instances");
|
||||||
console.warn(datalist);
|
console.warn(datalist);
|
||||||
const instancefetch=fetch("/instances.json")
|
const instancefetch = fetch("/instances.json")
|
||||||
.then(res=>res.json())
|
.then((res) => res.json())
|
||||||
.then(
|
.then(
|
||||||
(json: {
|
(
|
||||||
|
json: {
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
descriptionLong?: string;
|
descriptionLong?: string;
|
||||||
|
@ -180,51 +165,51 @@ const instancefetch=fetch("/instances.json")
|
||||||
url?: string;
|
url?: string;
|
||||||
display?: boolean;
|
display?: boolean;
|
||||||
online?: boolean;
|
online?: boolean;
|
||||||
uptime: { alltime: number; daytime: number; weektime: number };
|
uptime: {alltime: number; daytime: number; weektime: number};
|
||||||
urls: {
|
urls: {
|
||||||
wellknown: string;
|
wellknown: string;
|
||||||
api: string;
|
api: string;
|
||||||
cdn: string;
|
cdn: string;
|
||||||
gateway: string;
|
gateway: string;
|
||||||
login?: string;
|
login?: string;
|
||||||
}
|
};
|
||||||
}[]
|
}[],
|
||||||
)=>{
|
) => {
|
||||||
instances = json;
|
instances = json;
|
||||||
if(datalist){
|
if (datalist) {
|
||||||
console.warn(json);
|
console.warn(json);
|
||||||
const instancein = document.getElementById("instancein") as HTMLInputElement;
|
const instancein = document.getElementById("instancein") as HTMLInputElement;
|
||||||
if(instancein && instancein.value === ""){
|
if (instancein && instancein.value === "") {
|
||||||
instancein.value = json[0].name;
|
instancein.value = json[0].name;
|
||||||
}
|
}
|
||||||
for(const instance of json){
|
for (const instance of json) {
|
||||||
if(instance.display === false){
|
if (instance.display === false) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const option = document.createElement("option");
|
const option = document.createElement("option");
|
||||||
option.disabled = !instance.online;
|
option.disabled = !instance.online;
|
||||||
option.value = instance.name;
|
option.value = instance.name;
|
||||||
if(instance.url){
|
if (instance.url) {
|
||||||
stringURLMap.set(option.value, instance.url);
|
stringURLMap.set(option.value, instance.url);
|
||||||
if(instance.urls){
|
if (instance.urls) {
|
||||||
stringURLsMap.set(instance.url, instance.urls);
|
stringURLsMap.set(instance.url, instance.urls);
|
||||||
}
|
}
|
||||||
}else if(instance.urls){
|
} else if (instance.urls) {
|
||||||
stringURLsMap.set(option.value, instance.urls);
|
stringURLsMap.set(option.value, instance.urls);
|
||||||
}else{
|
} else {
|
||||||
option.disabled = true;
|
option.disabled = true;
|
||||||
}
|
}
|
||||||
if(instance.description){
|
if (instance.description) {
|
||||||
option.label = instance.description;
|
option.label = instance.description;
|
||||||
}else{
|
} else {
|
||||||
option.label = instance.name;
|
option.label = instance.name;
|
||||||
}
|
}
|
||||||
datalist.append(option);
|
datalist.append(option);
|
||||||
}
|
}
|
||||||
checkInstance("");
|
checkInstance("");
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
const stringURLMap = new Map<string, string>();
|
const stringURLMap = new Map<string, string>();
|
||||||
|
|
||||||
const stringURLsMap = new Map<
|
const stringURLsMap = new Map<
|
||||||
|
@ -236,9 +221,9 @@ const stringURLsMap = new Map<
|
||||||
gateway: string;
|
gateway: string;
|
||||||
login?: string;
|
login?: string;
|
||||||
}
|
}
|
||||||
>();
|
>();
|
||||||
export async function getapiurls(str: string): Promise<
|
export async function getapiurls(str: string): Promise<
|
||||||
{
|
| {
|
||||||
api: string;
|
api: string;
|
||||||
cdn: string;
|
cdn: string;
|
||||||
gateway: string;
|
gateway: string;
|
||||||
|
@ -246,31 +231,29 @@ export async function getapiurls(str: string): Promise<
|
||||||
login: string;
|
login: string;
|
||||||
}
|
}
|
||||||
| false
|
| false
|
||||||
>{
|
> {
|
||||||
function appendApi(str:string){
|
function appendApi(str: string) {
|
||||||
return str.includes("api")?"" : (str.endsWith("/")? "api" : "/api");
|
return str.includes("api") ? "" : str.endsWith("/") ? "api" : "/api";
|
||||||
}
|
}
|
||||||
if(!URL.canParse(str)){
|
if (!URL.canParse(str)) {
|
||||||
const val = stringURLMap.get(str);
|
const val = stringURLMap.get(str);
|
||||||
if(stringURLMap.size===0){
|
if (stringURLMap.size === 0) {
|
||||||
await new Promise<void>(res=>{
|
await new Promise<void>((res) => {
|
||||||
setInterval(()=>{
|
setInterval(() => {
|
||||||
if(stringURLMap.size!==0){
|
if (stringURLMap.size !== 0) {
|
||||||
res();
|
res();
|
||||||
}
|
}
|
||||||
},100);
|
}, 100);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if(val){
|
if (val) {
|
||||||
str = val;
|
str = val;
|
||||||
}else{
|
} else {
|
||||||
const val = stringURLsMap.get(str);
|
const val = stringURLsMap.get(str);
|
||||||
if(val){
|
if (val) {
|
||||||
const responce = await fetch(
|
const responce = await fetch(val.api + (val.api.endsWith("/") ? "" : "/") + "ping");
|
||||||
val.api + (val.api.endsWith("/") ? "" : "/") + "ping"
|
if (responce.ok) {
|
||||||
);
|
if (val.login) {
|
||||||
if(responce.ok){
|
|
||||||
if(val.login){
|
|
||||||
return val as {
|
return val as {
|
||||||
wellknown: string;
|
wellknown: string;
|
||||||
api: string;
|
api: string;
|
||||||
|
@ -278,7 +261,7 @@ export async function getapiurls(str: string): Promise<
|
||||||
gateway: string;
|
gateway: string;
|
||||||
login: string;
|
login: string;
|
||||||
};
|
};
|
||||||
}else{
|
} else {
|
||||||
val.login = val.api;
|
val.login = val.api;
|
||||||
return val as {
|
return val as {
|
||||||
wellknown: string;
|
wellknown: string;
|
||||||
|
@ -292,43 +275,38 @@ export async function getapiurls(str: string): Promise<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(str.at(-1) !== "/"){
|
if (str.at(-1) !== "/") {
|
||||||
str += "/";
|
str += "/";
|
||||||
}
|
}
|
||||||
let api: string;
|
let api: string;
|
||||||
try{
|
try {
|
||||||
const info = await fetch(`${str}.well-known/spacebar`).then(x=>x.json()
|
const info = await fetch(`${str}.well-known/spacebar`).then((x) => x.json());
|
||||||
);
|
|
||||||
api = info.api;
|
api = info.api;
|
||||||
}catch{
|
} catch {
|
||||||
api=str;
|
api = str;
|
||||||
}
|
}
|
||||||
if(!URL.canParse(api)){
|
if (!URL.canParse(api)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const url = new URL(api);
|
const url = new URL(api);
|
||||||
try{
|
try {
|
||||||
const info = await fetch(
|
const info = await fetch(
|
||||||
`${api}${
|
`${api}${url.pathname.includes("api") ? "" : "api"}/policies/instance/domains`,
|
||||||
url.pathname.includes("api") ? "" : "api"
|
).then((x) => x.json());
|
||||||
}/policies/instance/domains`
|
|
||||||
).then(x=>x.json());
|
|
||||||
const apiurl = new URL(info.apiEndpoint);
|
const apiurl = new URL(info.apiEndpoint);
|
||||||
return{
|
return {
|
||||||
api: info.apiEndpoint+appendApi(apiurl.pathname),
|
api: info.apiEndpoint + appendApi(apiurl.pathname),
|
||||||
gateway: info.gateway,
|
gateway: info.gateway,
|
||||||
cdn: info.cdn,
|
cdn: info.cdn,
|
||||||
wellknown: str,
|
wellknown: str,
|
||||||
login: info.apiEndpoint+appendApi(apiurl.pathname),
|
login: info.apiEndpoint + appendApi(apiurl.pathname),
|
||||||
};
|
};
|
||||||
}catch{
|
} catch {
|
||||||
const val = stringURLsMap.get(str);
|
const val = stringURLsMap.get(str);
|
||||||
if(val){
|
if (val) {
|
||||||
const responce = await fetch(
|
const responce = await fetch(val.api + (val.api.endsWith("/") ? "" : "/") + "ping");
|
||||||
val.api + (val.api.endsWith("/") ? "" : "/") + "ping"
|
if (responce.ok) {
|
||||||
);
|
if (val.login) {
|
||||||
if(responce.ok){
|
|
||||||
if(val.login){
|
|
||||||
return val as {
|
return val as {
|
||||||
wellknown: string;
|
wellknown: string;
|
||||||
api: string;
|
api: string;
|
||||||
|
@ -336,7 +314,7 @@ export async function getapiurls(str: string): Promise<
|
||||||
gateway: string;
|
gateway: string;
|
||||||
login: string;
|
login: string;
|
||||||
};
|
};
|
||||||
}else{
|
} else {
|
||||||
val.login = val.api;
|
val.login = val.api;
|
||||||
return val as {
|
return val as {
|
||||||
wellknown: string;
|
wellknown: string;
|
||||||
|
@ -351,10 +329,10 @@ export async function getapiurls(str: string): Promise<
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function checkInstance(instance: string){
|
export async function checkInstance(instance: string) {
|
||||||
await instancefetch;
|
await instancefetch;
|
||||||
const verify = document.getElementById("verify");
|
const verify = document.getElementById("verify");
|
||||||
try{
|
try {
|
||||||
verify!.textContent = I18n.getTranslation("login.checking");
|
verify!.textContent = I18n.getTranslation("login.checking");
|
||||||
const instanceValue = instance;
|
const instanceValue = instance;
|
||||||
const instanceinfo = (await getapiurls(instanceValue)) as {
|
const instanceinfo = (await getapiurls(instanceValue)) as {
|
||||||
|
@ -365,55 +343,57 @@ export async function checkInstance(instance: string){
|
||||||
login: string;
|
login: string;
|
||||||
value: string;
|
value: string;
|
||||||
};
|
};
|
||||||
if(instanceinfo){
|
if (instanceinfo) {
|
||||||
instanceinfo.value = instanceValue;
|
instanceinfo.value = instanceValue;
|
||||||
localStorage.setItem("instanceinfo", JSON.stringify(instanceinfo));
|
localStorage.setItem("instanceinfo", JSON.stringify(instanceinfo));
|
||||||
verify!.textContent = I18n.getTranslation("login.allGood");
|
verify!.textContent = I18n.getTranslation("login.allGood");
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if(checkInstance.alt){
|
if (checkInstance.alt) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
checkInstance.alt();
|
checkInstance.alt();
|
||||||
}
|
}
|
||||||
setTimeout((_: any)=>{
|
setTimeout((_: any) => {
|
||||||
console.log(verify!.textContent);
|
console.log(verify!.textContent);
|
||||||
verify!.textContent = "";
|
verify!.textContent = "";
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}else{
|
} else {
|
||||||
verify!.textContent = I18n.getTranslation("login.invalid");
|
verify!.textContent = I18n.getTranslation("login.invalid");
|
||||||
}
|
}
|
||||||
}catch{
|
} catch {
|
||||||
console.log("catch");
|
console.log("catch");
|
||||||
verify!.textContent = I18n.getTranslation("login.invalid");
|
verify!.textContent = I18n.getTranslation("login.invalid");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export function getInstances(){
|
export function getInstances() {
|
||||||
return instances;
|
return instances;
|
||||||
}
|
}
|
||||||
export class SW{
|
export class SW {
|
||||||
static worker:undefined|ServiceWorker;
|
static worker: undefined | ServiceWorker;
|
||||||
static setMode(mode:"false"|"offlineOnly"|"true"){
|
static setMode(mode: "false" | "offlineOnly" | "true") {
|
||||||
localStorage.setItem("SWMode",mode);
|
localStorage.setItem("SWMode", mode);
|
||||||
if(this.worker){
|
if (this.worker) {
|
||||||
this.worker.postMessage({data:mode,code:"setMode"});
|
this.worker.postMessage({data: mode, code: "setMode"});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static checkUpdate(){
|
static checkUpdate() {
|
||||||
if(this.worker){
|
if (this.worker) {
|
||||||
this.worker.postMessage({code:"CheckUpdate"});
|
this.worker.postMessage({code: "CheckUpdate"});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static forceClear(){
|
static forceClear() {
|
||||||
if(this.worker){
|
if (this.worker) {
|
||||||
this.worker.postMessage({code:"ForceClear"});
|
this.worker.postMessage({code: "ForceClear"});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("serviceWorker" in navigator){
|
if ("serviceWorker" in navigator) {
|
||||||
navigator.serviceWorker.register("/service.js", {
|
navigator.serviceWorker
|
||||||
|
.register("/service.js", {
|
||||||
scope: "/",
|
scope: "/",
|
||||||
}).then((registration) => {
|
})
|
||||||
let serviceWorker:ServiceWorker|undefined;
|
.then((registration) => {
|
||||||
|
let serviceWorker: ServiceWorker | undefined;
|
||||||
if (registration.installing) {
|
if (registration.installing) {
|
||||||
serviceWorker = registration.installing;
|
serviceWorker = registration.installing;
|
||||||
console.log("installing");
|
console.log("installing");
|
||||||
|
@ -424,13 +404,13 @@ if ("serviceWorker" in navigator){
|
||||||
serviceWorker = registration.active;
|
serviceWorker = registration.active;
|
||||||
console.log("active");
|
console.log("active");
|
||||||
}
|
}
|
||||||
SW.worker=serviceWorker;
|
SW.worker = serviceWorker;
|
||||||
SW.setMode(localStorage.getItem("SWMode") as "false"|"offlineOnly"|"true");
|
SW.setMode(localStorage.getItem("SWMode") as "false" | "offlineOnly" | "true");
|
||||||
if (serviceWorker) {
|
if (serviceWorker) {
|
||||||
console.log(serviceWorker.state);
|
console.log(serviceWorker.state);
|
||||||
serviceWorker.addEventListener("statechange", (_) => {
|
serviceWorker.addEventListener("statechange", (_) => {
|
||||||
console.log(serviceWorker.state);
|
console.log(serviceWorker.state);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue