added a home screen to jank client

This commit is contained in:
MathMan05 2024-08-29 20:56:52 -05:00
parent 04e3ac9955
commit f2480130c1
8 changed files with 407 additions and 10 deletions

62
.dist/home.js Normal file
View file

@ -0,0 +1,62 @@
import { mobile } from "./login.js";
console.log(mobile);
const serverbox = document.getElementById("instancebox");
fetch("/instances.json").then(_ => _.json()).then((json) => {
console.warn(json);
for (const instance of json) {
if (instance.display === false) {
continue;
}
const div = document.createElement("div");
div.classList.add("flexltr", "instance");
if (instance.image) {
const img = document.createElement("img");
img.src = instance.image;
div.append(img);
}
const statbox = document.createElement("div");
statbox.classList.add("flexttb");
{
const textbox = document.createElement("div");
textbox.classList.add("flexttb", "instatancetextbox");
const title = document.createElement("h2");
title.innerText = instance.name;
if (instance.online !== undefined) {
const status = document.createElement("span");
status.innerText = instance.online ? "Online" : "Offline";
status.classList.add("instanceStatus");
title.append(status);
}
textbox.append(title);
if (instance.description || instance.descriptionLong) {
const p = document.createElement("p");
if (instance.descriptionLong) {
p.innerText = instance.descriptionLong;
}
else {
p.innerText = instance.description;
}
textbox.append(p);
}
statbox.append(textbox);
}
{
const stats = document.createElement("div");
stats.classList.add("flexltr");
const span = document.createElement("span");
span.innerText = `Uptime: All time: ${Math.floor(instance.uptime.alltime * 100)}% This week: ${Math.floor(instance.uptime.weektime * 100)}% Today: ${Math.floor(instance.uptime.daytime * 100)}%`;
stats.append(span);
statbox.append(stats);
}
div.append(statbox);
div.onclick = _ => {
if (instance.online) {
window.location.href = "/register.html?instance=" + encodeURI(instance.name);
}
else {
alert("Instance is offline, can't connect");
}
};
serverbox.append(div);
}
});

View file

@ -419,7 +419,6 @@ if (datalist) {
console.warn(json);
if (instancein && instancein.value === "") {
instancein.value = json[0].name;
setTimeout(checkInstance, 10);
}
for (const instance of json) {
if (instance.display === false) {
@ -447,5 +446,6 @@ if (datalist) {
}
datalist.append(option);
}
checkInstance("");
});
}

View file

@ -4,7 +4,8 @@ const compression = require('compression')
const express = require('express');
const fs = require('fs');
const app = express();
const instances=require("./webpage/instances.json")
const instances=require("./webpage/instances.json");
const stats=require("./stats.js");
const instancenames=new Map();
for(const instance of instances){
instancenames.set(instance.name,instance);
@ -12,7 +13,6 @@ for(const instance of instances){
app.use(compression())
fetch("https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/instances/instances.json").then(_=>_.json()).then(json=>{
for(const instance of json){
console.log(instance);
if(!instancenames.has(instance.name)){
instances.push(instance);
}else{
@ -24,6 +24,7 @@ fetch("https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/instan
}
}
}
stats.observe(instances)
})
app.use("/getupdates",(req, res) => {
@ -145,6 +146,10 @@ async function inviteres(req,res){
app.use('/services/oembed', (req, res) => {
inviteres(req, res);
})
app.use("/uptime",(req,res)=>{
const uptime=stats.uptime[req.query.name];
res.send(uptime);
})
app.use('/', async (req, res) => {
const scheme = req.secure ? 'https' : 'http';
const host=`${scheme}://${req.get("Host")}`;
@ -156,13 +161,14 @@ app.use('/', async (req, res) => {
}else{
console.log(req);
}
if(req.path==="/"){
res.sendFile(`./webpage/home.html`, {root: __dirname});
}
if(debugging&&req.path.startsWith("/service.js")){
res.send("dud");
return;
}
if(req.path.startsWith("/instances.json")){
console.log("grabbed")
res.send(JSON.stringify(instances));
return;
}
@ -189,3 +195,5 @@ app.use('/', async (req, res) => {
const PORT = process.env.PORT || +process.argv[1] || 8080;
app.listen(PORT, () => {});
console.log("this ran :P");
exports.getapiurls=getapiurls;

142
stats.js Normal file
View file

@ -0,0 +1,142 @@
const index = require('./index.js');
const fs=require("fs");
let uptimeObject={};
if(fs.existsSync("./uptime.json")){
try{
uptimeObject=JSON.parse(fs.readFileSync('./uptime.json', 'utf8'));
}catch{
uptimeObject={};
}
}
async function observe(instances){
const active=new Set();
async function resolveinstance(instance){
calcStats(instance);
let api;
if(instance.urls){
api=instance.urls.api;
}else if(instance.url){
const urls=await index.getapiurls(instance.url);
if(urls){
api=urls.api;
}
}
if(!api||api===""){
setSatus(instance,false);
console.warn(instance.name+" does not resolve api URL");
setTimeout(_=>{resolveinstance(instance)},1000*60*30,);
return
}
active.add(instance.name);
api+=api.endsWith("/")?"":"/"
function check(){
fetch(api+"ping").then(_=>{
setSatus(instance,_.ok);
})
}
setTimeout(
_=>{
check();
setInterval(_=>{
check();
},1000*60*30)
},Math.random()*1000*60*10
)
}
const promlist=[];
for(const instance of instances){
promlist.push(resolveinstance(instance));
}
await Promise.allSettled(promlist);
for(const key of Object.keys(uptimeObject)){
if(!active.has(key)){
setSatus(key,false);
}
}
}
function calcStats(instance){
let obj=uptimeObject[instance.name];
if(!obj) return;
const day=Date.now()-1000*60*60*24;
const week=Date.now()-1000*60*60*24*7;
let alltime=-1;
let totalTimePassed=0;
let laststamp=0;
let daytime=-1;
let weektime=-1;
let online=false;
for(const thing of obj){
const stamp=thing.time;
if(alltime===-1){
laststamp=stamp;
alltime=0;
}
const timepassed=stamp-laststamp;
totalTimePassed+=timepassed;
alltime+=online*timepassed;
if(stamp>week){
if(weektime===-1){
weektime=online*(stamp-week);
}else{
weektime+=online*timepassed;
}
if(stamp>day){
if(daytime===-1){
daytime=online*(stamp-day);
}else{
daytime+=online*timepassed;
}
}
}
online=thing.online;
}
instance.online=online;
const timepassed=Date.now()-laststamp;
totalTimePassed+=timepassed;
daytime+=online*timepassed;
weektime+=online*timepassed;
alltime+=online*timepassed;
alltime/=totalTimePassed;
if(timepassed>1000*60*60*24){
daytime/=1000*60*60*24;
if(timepassed>1000*60*60*24*7){
weektime/=1000*60*60*24*7;
}else{
weektime=alltime;
}
}else{
weektime=alltime
daytime=alltime;
}
instance.uptime={daytime,weektime,alltime}
}
/**
* @param {string} name
* @param {boolean} status
*/
function setSatus(instance,status){
const name=instance.name;
let obj=uptimeObject[name];
let needSetting=false;
if(!obj){
obj=[];
uptimeObject[name]=obj;
needSetting=true;
}else{
if(obj[obj.length-1].online!==status){
needSetting=true;
}
}
if(needSetting){
obj.push({time:Date.now(),online:status});
updatejson();
}
calcStats(instance);
}
function updatejson(){
fs.writeFile('./uptime.json',JSON.stringify(uptimeObject),_=>{});
}
exports.observe=observe;
exports.uptime=uptimeObject;

48
webpage/home.html Normal file
View file

@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jank Client</title>
<meta content="Jank Client" property="og:title" />
<meta content="A spacebar client that has DMs, replying and more" property="og:description" />
<meta content="/logo.webp" property="og:image" />
<meta content="#4b458c" data-react-helmet="true" name="theme-color" />
<link href="/style.css" rel="stylesheet" type="text/css" />
<link href="/themes.css" rel="stylesheet" type="text/css" id="lightcss"/>
</head>
<body>
<div id="titleDiv">
<img src="/logo.svg" width="40">
<h1 id="pageTitle">Jank Client</h1>
<a href="https://sb-jankclient.vanillaminigames.net/invite/USgYJo?instance=https%3A%2F%2Fspacebar.chat" class="TitleButtons"><h1>Spacebar Guild</h1></a>
<a href="https://github.com/MathMan05/JankClient" class="TitleButtons"><h1>Github</h1></a>
</div>
<div class="flexttb">
<div class="flexttb pagehead"><h1>Welcome to Jank Client</h1></div>
<div class="pagebox">
<p>Jank Client is a spacebar compatible client seeking to be as good as it can be with many features including:</p>
<ul>
<li>Direct Messaging</li>
<li>Reactions support</li>
<li>Invites</li>
<li>Account switching</li>
<li>User settings</li>
</ul>
</div>
<div class="pagebox">
<h2>Spacebar compatible Instances:</h2>
<div id="instancebox">
</div>
</div>
<div class="pagebox">
<h2>Contribute to Jank Client</h2>
<p>We always appreciate some help, wether that be in the form of bug reports, or code, or even just pointing out some typos.</p><br>
</a><a href="https://github.com/MathMan05/JankClient" class="TitleButtons"><h1>Github</h1></a>
</div>
</div>
</body>
<script src="/home.js" type="module" ></script>
</html>

64
webpage/home.ts Normal file
View file

@ -0,0 +1,64 @@
import {mobile} from "./login.js";
console.log(mobile);
const serverbox=document.getElementById("instancebox") as HTMLDivElement;
fetch("/instances.json").then(_=>_.json()).then((json:{name:string,description?:string,descriptionLong?:string,image?:string,url?:string,display?:boolean,online?:boolean,
uptime:{alltime:number,daytime:number,weektime:number},
urls:{wellknown:string,api:string,cdn:string,gateway:string,login?:string}}[])=>{
console.warn(json);
for(const instance of json){
if(instance.display===false){
continue;
}
const div=document.createElement("div");
div.classList.add("flexltr","instance");
if(instance.image){
const img=document.createElement("img");
img.src=instance.image;
div.append(img);
}
const statbox=document.createElement("div");
statbox.classList.add("flexttb");
{
const textbox=document.createElement("div");
textbox.classList.add("flexttb","instatancetextbox");
const title=document.createElement("h2");
title.innerText=instance.name;
if(instance.online!==undefined){
const status=document.createElement("span");
status.innerText=instance.online?"Online":"Offline";
status.classList.add("instanceStatus");
title.append(status);
}
textbox.append(title);
if(instance.description||instance.descriptionLong){
const p=document.createElement("p");
if(instance.descriptionLong){
p.innerText=instance.descriptionLong;
}else{
p.innerText=instance.description;
}
textbox.append(p);
}
statbox.append(textbox)
}
{
const stats=document.createElement("div");
stats.classList.add("flexltr");
const span=document.createElement("span");
span.innerText=`Uptime: All time: ${Math.floor(instance.uptime.alltime*100)}% This week: ${Math.floor(instance.uptime.weektime*100)}% Today: ${Math.floor(instance.uptime.daytime*100)}%`
stats.append(span);
statbox.append(stats);
}
div.append(statbox);
div.onclick=_=>{
if(instance.online){
window.location.href="/register.html?instance="+encodeURI(instance.name);
}else{
alert("Instance is offline, can't connect");
}
}
serverbox.append(div);
}
})

View file

@ -409,7 +409,6 @@ if(datalist){
console.warn(json);
if(instancein&&instancein.value===""){
instancein.value=json[0].name;
setTimeout(checkInstance,10);
}
for(const instance of json){
if(instance.display===false){
@ -434,5 +433,6 @@ if(datalist){
}
datalist.append(option);
}
checkInstance("");
})
}

View file

@ -1257,6 +1257,7 @@ span {
/* padding-bottom: .1in; */
align-items: flex-start;
width: 100%;
height: 100%;
}
.settingbuttons{
padding-top:.075in;
@ -1958,12 +1959,12 @@ form div{
flex-direction: row;
align-items: center;
width: 100%;
padding: .03in .15in;
padding: .03in .1in;
background: var(--profile-bg);
border-bottom: solid var(--black);
position: absolute;
justify-content: center;
}
#TitleButtons{
.TitleButtons{
color:var(--primary-text);
margin-left: .03in;
padding: .05in;
@ -1971,10 +1972,82 @@ form div{
border:solid .03in var(--black);
box-shadow: 0 0 .07in var(--shadow);
transition: box-shadow .3s;
display: block;
width: fit-content;
}
#TitleButtons:hover{
.TitleButtons:hover{
box-shadow: 0 0 .01in var(--shadow);
}
#pageTitle{
margin-right:.1in;
}
.pagehead{
background: var(--channel-hover);
height:1in;
display: flex;
align-items: center;
border-bottom: solid var(--black);
justify-content: center;
font-size: .25in;
flex-shrink: 0;
}
.pagebox{
padding: .06in;
background: var(--channel-hover);
margin: .1in;
min-height:2in;
border-radius:.1in;
box-sizing: border-box;
border: solid .04in black;
border-left: solid .05in darkblue;
box-shadow: .03in .04in .1in var(--black);
flex-shrink: 0;
}
#instancebox{
display:flex;
flex-direction: row;
flex-wrap: wrap;
width: fit-content;
}
.instance{
img{
width:.6in;
height:.6in;
margin-right:.1in;
border-radius:.2in;
border:solid .03in var(--black);
}
h2{
font-size:.25in;
}
flex-grow:0;
width:4.8in;
height: 1.6in;
background:var(--button-bg);
border-radius:.1in;
padding:.06in;
margin:.05in;
border:solid .04in var(--black);
box-shadow:0 0 .1in var(--shadow);
display: flex;
justify-content: center;
cursor:pointer;
user-select:none;
}
.instatancetextbox{
background:var(--message-bg-hover);
border-radius:.1in;
padding:0.03in .05in;
border:solid .03in var(--black);
height: 1.2in;
display:flex;
justify-content: center;
width:3.9in;
margin-bottom:.1in;
}
.instanceStatus{
font-size:.125in;
font-weight:100;
margin-left:.05in;
}