switching networks works, established server functions

This commit is contained in:
metacryst
2026-01-16 05:22:52 -06:00
parent d2982543d0
commit d3df5bb6cb
31 changed files with 410 additions and 310 deletions

4
db/personal/1/db.json Normal file
View File

@@ -0,0 +1,4 @@
{
"nodes": {},
"edges": {}
}

View File

@@ -7,15 +7,36 @@ img(`db/images/${networks[i].logo}`, "2.25em", "2.25em")
}
})
.cursor("default")
.DEFAULT()
.Default()
.opacity(0)
.borderLeft(0)
.paddingLeft(10, px)
.HOVERED()
.Hovered()
.opacity(0.8)
.classStyle("selected")
.borderLeft("1px solid var(--accent)")
.paddingLeft(9, px)
.Watch(global.currentNetwork)
.If((val) => val === this.getAttribute("network"))
.borderLeft("1px solid black")
.paddingLeft(9, px)
.ElseIf((val) => val !== this.getAttribute("network"))
.borderLeft("0")
.paddingLeft(10, px)
render() {
ZStack(() => {
Watch(global.currentNetwork)
.Case("Dashboard", () => {
Dashboard()
})
.Case("People", () => {
People()
})
})
.overflow("scroll")
.position("absolute")
.onEvent("resize", () => {
this.rerender()
})

View File

@@ -29,7 +29,7 @@ export default class AuthHandler {
}
getProfile(req, res) {
const token = req.cookies.auth_token;
const token = req.cookies?.auth_token;
if (!token) return res.status(401).send({ error: "No auth token" });
try {
@@ -42,7 +42,13 @@ export default class AuthHandler {
return db.networks.get(c.to)
})
res.send({ email: user.email, name: user.firstName + " " + user.lastName, networks: userOrgs});
res.send({
id: user.id,
email: user.email,
name: user.firstName + " " + user.lastName,
networks: userOrgs,
apps: user.apps
});
} catch (e) {
console.error("Error getting profile: ", e)
res.status(401).send({ error: "Invalid token" });

View File

@@ -3,9 +3,13 @@ import chalk from 'chalk';
import path from 'path';
import {nodeModels, edgeModels} from './model/import.js'
import Edge from "./model/edge.js"
import { fileURLToPath } from "url"
const __dirname = path.dirname(fileURLToPath(import.meta.url))
export default class Database {
PERSONAL_DATA_PATH = path.join(__dirname, '../../db/personal')
nodes = new Array(10000).fill(0);
edges = new Array(10000).fill(0);

View File

@@ -1,3 +1,5 @@
import path from 'path';
import fs from 'fs';
import argon2 from 'argon2';
import { z } from 'zod';
@@ -15,6 +17,7 @@ export default class Member {
firstName: z.string(),
lastName: z.string(),
password: z.string(),
apps: z.array(z.string()),
created: z.string()
})
@@ -48,6 +51,17 @@ export default class Member {
return members
}
async getPersonalData(id) {
const filePath = path.join(global.db.PERSONAL_DATA_PATH, id, "db.json");
const [raw] = await Promise.all([
fs.promises.readFile(filePath, "utf8"),
]);
const result = raw.trim() ? JSON.parse(raw) : [];
return result
}
getByID(id) {
if(typeof id === 'string') {
id = id.split("-")[1]

View File

@@ -38,10 +38,25 @@ class Server {
router.post('/free', this.newUserSubmission)
router.get('/db/images/*', this.getUserImage)
router.get('/app/orgdata/*', this.getOrgData)
router.get('/app/mydata/*', this.getPersonalData)
router.get('/*', this.get)
return router
}
getPersonalData = async (req, res, next) => {
try {
const memberId = req.params[0]
let data = await global.db.members.getPersonalData(memberId)
res.json({
data
});
} catch (err) {
next(err);
}
}
getOrgData = async (req, res, next) => {
try {
const networkId = req.params[0]

View File

@@ -2,6 +2,7 @@ import { WebSocket, WebSocketServer } from 'ws';
import { z } from 'zod';
import jwt from 'jsonwebtoken';
import * as serverFunctions from "../../ui/_/code/bridge/serverFunctions.js"
import ForumHandler from "./handlers/ForumHandler.js"
import MessagesHandler from "./handlers/MessagesHandler.js"
@@ -9,7 +10,7 @@ export default class Socket {
wss;
messageSchema = z.object({
id: z.string(),
app: z.string(),
app: z.string().optional(),
operation: z.string().optional(),
msg: z.union([
z.object({}).passthrough(), // allows any object
@@ -67,7 +68,7 @@ export default class Socket {
const text = msg.toString();
const req = JSON.parse(text);
if(!this.messageSchema.safeParse(req).success) throw new Error("Socket.handleMessage: Incoming ws message has incorrect format!")
let responseData;
switch (req.app) {
case "FORUM":
@@ -79,7 +80,12 @@ export default class Socket {
break;
default:
console.error("unknown ws message")
if(!req.app) {
let func = req.msg
responseData = serverFunctions[func.name](...args)
} else {
console.error("unknown ws message")
}
}
let response = {

View File

@@ -0,0 +1,38 @@
const IS_NODE =
typeof process !== "undefined" &&
process.versions?.node != null
async function bridgeSend(name, args) {
// Example browser implementation: send function call to server
const res = await global.Socket.send({
name: name,
args: args
})
const json = await res.json()
if (!res.ok) throw new Error(json.error)
return json.result
}
/**
* Wraps an object of functions so that:
* - Node calls the real function
* - Browser calls bridgeSend
*/
export function createBridge(funcs) {
return new Proxy(funcs, {
get(target, prop) {
const orig = target[prop]
if (typeof orig !== "function") return orig
return function (...args) {
if (IS_NODE) {
return orig(...args)
} else {
return bridgeSend(prop, args)
}
}
}
})
}

View File

@@ -0,0 +1,11 @@
import fs from "fs"
import { createBridge } from "./bridge.js"
const handlers = {
getProfile(one, two) {
fs.writeFileSync("output.txt", `${one} ${two}`)
return "written to disk"
},
}
export const { getProfile } = createBridge(handlers)

View File

@@ -1,6 +1,7 @@
/*
Sam Russell
Captured Sun
1.16.26 - Moving nav event dispatch out of pushState, adding null feature to attr()
1.5.26 - Switching verticalAlign and horizontalAlign names, adding borderVertical and Horizontal
12.26.25 - State for arrays, nested objects. State for stacks (Shadow-only)
12.17.25 - [Hyperia] - adding width, height functions. adding "e" to onClick. adding the non-window $$ funcs.
@@ -27,7 +28,6 @@ let oldPushState = history.pushState;
history.pushState = function pushState() {
let ret = oldPushState.apply(this, arguments);
window.dispatchEvent(new Event('pushstate'));
window.dispatchEvent(new Event('navigate'));
return ret;
};
@@ -53,8 +53,8 @@ window.setQuery = function(key, value) {
};
window.navigateTo = function(url) {
window.dispatchEvent(new Event('navigate'));
window.history.pushState({}, '', url);
window.dispatchEvent(new Event('navigate'));
}
window.setLocation = function(url) {
@@ -1197,7 +1197,11 @@ HTMLElement.prototype.attr = function(attributes) {
}
for (const [key, value] of Object.entries(attributes)) {
this.setAttribute(key, value);
if(value === null) {
this.removeAttribute(key)
} else {
this.setAttribute(key, value);
}
}
return this;
};

View File

@@ -1,62 +1,61 @@
class Connection {
connectionTries = 0
connectionTries = 0;
ws;
linkCreated;
wsStatus;
receiveCB;
constructor(receiveCB) {
this.init()
this.receiveCB = receiveCB
this.receiveCB = receiveCB;
}
init() {
if(window.location.hostname.includes("local")) {
this.ws = new WebSocket("ws://" + window.location.host)
} else {
this.ws = new WebSocket("wss://" + window.location.hostname + window.location.pathname)
}
this.ws.addEventListener('open', () => {
this.connectionTries = 0
console.log("Websocket connection established.");
this.ws.addEventListener('message', this.receiveCB)
init = async () => {
return new Promise((resolve, reject) => {
const url = window.location.hostname.includes("local")
? "ws://" + window.location.host
: "wss://" + window.location.hostname + window.location.pathname;
this.ws = new WebSocket(url);
this.ws.addEventListener('open', () => {
this.connectionTries = 0;
console.log("WebSocket connection established.");
this.ws.addEventListener('message', this.receiveCB);
resolve(this.ws); // resolve when open
});
this.ws.addEventListener('close', () => {
console.log('WebSocket closed');
this.checkOpen(); // attempt reconnection
});
this.ws.addEventListener('error', (err) => {
console.error('WebSocket error', err);
reject(err); // reject if error occurs
});
});
this.ws.addEventListener("close", () => {
this.checkOpen();
console.log('Websocket Closed')
})
}
async checkOpen() {
checkOpen = async () => {
if (this.ws.readyState === WebSocket.OPEN) {
return true
return true;
} else {
await this.sleep(this.connectionTries < 20 ? 5000 : 60000)
this.connectionTries++
console.log('Reestablishing connection')
this.init()
await this.sleep(this.connectionTries < 20 ? 5000 : 60000);
this.connectionTries++;
console.log('Reestablishing connection');
await this.init();
}
}
sleep = (time) => {
return new Promise(resolve => {
setTimeout(resolve, time);
});
}
sleep = (time) => new Promise(resolve => setTimeout(resolve, time));
send = (msg) => {
console.log("sending")
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(msg);
}
else if(this.connectionTries === 0) {
setTimeout(() => {
this.send(msg)
}, 100)
}
else {
console.error('No websocket connection: Cannot send message');
} else if (this.connectionTries === 0) {
setTimeout(() => this.send(msg), 100);
} else {
console.error('No WebSocket connection: Cannot send message');
}
}
}
export default Connection
export default Connection;

View File

@@ -10,6 +10,10 @@ export default class Socket {
this.connection = new Connection(this.receive);
}
async init() {
await this.connection.init()
}
isOpen() {
if(this.connection.checkOpen()) {
return true;

1
ui/_/code/ws/shim/fs.js Normal file
View File

@@ -0,0 +1 @@
export default {}

View File

@@ -20,6 +20,15 @@ class Dashboard extends Shadow {
render() {
VStack(() => {
if(window.location.pathname.startsWith("/my")) {
h1(global.profile.name);
return
}
else if(!window.location.pathname.includes("comalyr")) {
return
}
h1("Website Inquiries");
p("Contact Us")
@@ -41,7 +50,7 @@ class Dashboard extends Shadow {
.maxWidth(95, pct)
.gap(8);
window.currentNetwork.data.contact.forEach((entry) => {
global.currentNetwork.data.contact.forEach((entry) => {
HStack(() => {
this.cell("time", entry.time);
this.cell("fname", entry.fname);
@@ -74,7 +83,7 @@ class Dashboard extends Shadow {
.maxWidth(95, pct)
.gap(8);
window.currentNetwork.data.join.forEach((entry) => {
global.currentNetwork.data.join.forEach((entry) => {
HStack(() => {
this.cell("time", entry.time);
this.cell("fname", entry.fname);

View File

@@ -79,7 +79,7 @@ class Forum extends Shadow {
.fontSize(1, em)
.onKeyDown(function (e) {
if (e.key === "Enter") {
window.Socket.send({app: "FORUM", operation: "SEND", msg: {forum: "HY", text: this.value }})
global.Socket.send({app: "FORUM", operation: "SEND", msg: {forum: "HY", text: this.value }})
this.value = ""
}
})

View File

@@ -65,7 +65,7 @@ class Messages extends Shadow {
.fontSize(1, em)
.onKeyDown((e) => {
if (e.key === "Enter") {
window.Socket.send({app: "MESSAGES", operation: "SEND", msg: { conversation: `CONVERSATION-${this.selectedConvoID}`, text: e.target.value }})
global.Socket.send({app: "MESSAGES", operation: "SEND", msg: { conversation: `CONVERSATION-${this.selectedConvoID}`, text: e.target.value }})
e.target.value = ""
}
})
@@ -104,7 +104,7 @@ class Messages extends Shadow {
.color("var(--accent)")
.onKeyDown(function (e) {
if (e.key === "Enter") {
window.Socket.send({app: "MESSAGES", operation: "ADDCONVERSATION", msg: {email: this.value }})
global.Socket.send({app: "MESSAGES", operation: "ADDCONVERSATION", msg: {email: this.value }})
this.value = ""
}
})

View File

@@ -13,7 +13,7 @@ class MessagesPanel extends Shadow {
if(this.messages) {
for(let i=0; i<this.messages.length; i++) {
let message = this.messages[i]
let fromMe = window.profile.email === message.from.email
let fromMe = global.profile.email === message.from.email
VStack(() => {
HStack(() => {
p(message.from.firstName + " " + message.from.lastName)

View File

@@ -57,7 +57,7 @@ class MessagesSidebar extends Shadow {
let membersString = ""
for(let i=0; i<members.length; i++) {
let member = members[i]
if(member.email === window.profile.email) {
if(member.email === global.profile.email) {
continue;
}
if(members.length > 2) {

View File

@@ -5,8 +5,8 @@ class People extends Shadow {
.fontWeight("bold")
.marginBottom(2, em)
for(let i = 0; i < window.currentNetwork.data.members.length; i++) {
let member = window.currentNetwork.data.members[i]
for(let i = 0; i < global.currentNetwork.data.members.length; i++) {
let member = global.currentNetwork.data.members[i]
HStack(() => {
p(member.firstName + " " + member.lastName)
.width(10, pct)

View File

@@ -15,9 +15,9 @@ class AppMenu extends Shadow {
}
HStack(() => {
let currentNetwork = window.currentNetwork
let currentNetwork = global.currentNetwork
if(!currentNetwork) return
let currentApp = window.currentApp
let currentApp = global.currentApp
for(let i = 0; i < currentNetwork.apps.length; i++) {
let app = currentNetwork.apps[i]
@@ -27,7 +27,7 @@ class AppMenu extends Shadow {
.paddingBottom(currentApp === app ? 4 : 5, px)
.borderBottom(currentApp === app ? "1px solid var(--accent)" : "")
.onClick((done) => {
if(done) window.openApp(app)
if(done) global.openApp(app)
})
.onHover(function (hovering) {
if(hovering) {
@@ -49,6 +49,10 @@ class AppMenu extends Shadow {
// console.log("app hello?") // BUG: Quill is not acknowledging this event unless there is something else in the function body
this.rerender()
})
.onEvent("networkchange", () => {
// console.log(global.currentApp)
this.rerender()
})
.position("fixed")
.x(0).yBottom(0)
.width(100, vw)

View File

@@ -22,7 +22,7 @@ class AppWindow extends Shadow {
render() {
ZStack(() => {
switch(window.currentApp) {
switch(global.currentApp) {
case "Dashboard":
Dashboard()
break;
@@ -33,17 +33,20 @@ class AppWindow extends Shadow {
})
.overflow("scroll")
.position("absolute")
.onEvent("resize", () => {
this.rerender()
})
.width(window.innerWidth - this.calculateWidth(), px)
.height(window.innerHeight - this.calculateHeight(), px)
.background("var(--app)")
.x(this.calculateWidth(), px)
.yBottom(this.calculateHeight(), px)
.onEvent("resize", () => {
this.rerender()
})
.onEvent("appchange", () => {
this.rerender()
})
.onEvent("networkchange", () => {
this.rerender()
})
}
}

View File

@@ -8,7 +8,7 @@ class ProfileMenu extends Shadow {
p("Email: ")
.fontWeight("bold")
p(window.profile?.email)
p(global.profile?.email)
})
.gap(1, em)
@@ -16,7 +16,7 @@ class ProfileMenu extends Shadow {
p("Name: ")
.fontWeight("bold")
p(window.profile?.name)
p(global.profile?.name)
})
.gap(1, em)
@@ -37,8 +37,8 @@ class ProfileMenu extends Shadow {
.center()
.display("none")
.onAppear(async () => {
if(!window.profile) {
window.profile = await this.fetchProfile()
if(!global.profile) {
global.profile = await this.fetchProfile()
this.rerender()
}
})

View File

@@ -1,18 +1,43 @@
class Sidebar extends Shadow {
currentNetwork = null
toggleSelectedStyles(el) {
let currentlySelected = $("img[selected]")
if(currentlySelected) {
currentlySelected.removeAttribute("selected")
currentlySelected.style.borderLeft = ""
currentlySelected.style.paddingLeft = "10px"
}
el.setAttribute("selected", "")
el.style.borderLeft = "1px solid var(--accent)"
el.style.paddingLeft = "9px"
}
render() {
VStack(() => {
let selected = window.location.pathname.startsWith("/my")
img(document.documentElement.classList.contains("red") ? "/_/icons/quillblack.svg" : "/_/icons/quill.svg", "2.5em", "2.5em")
.marginTop(6, vh)
.marginBottom(2, vh)
.attr({selected: selected ? "" : null})
.paddingRight(0.5, em)
.paddingLeft(selected ? 9 : 10, px)
.borderLeft(selected ? "1px solid var(--accent)" : "0")
.onClick((done, e) => {
if(done) {
this.toggleSelectedStyles(e.target)
window.navigateTo("/my")
}
})
let networks = window.profile.networks
let networks = global.profile.networks
for(let i=0; i<networks.length; i++) {
let selected = window.location.pathname.startsWith("/" + networks[i].abbreviation)
img(`/db/images/${networks[i].logo}`, "2.25em", "2.25em")
.marginTop(3, vh)
.attr({selected: selected ? "" : null})
.paddingRight(0.5, em)
.paddingLeft(selected ? 9 : 10, px)
.borderLeft(selected ? "1px solid var(--accent)" : "0")
@@ -23,11 +48,10 @@ class Sidebar extends Shadow {
this.style.opacity = ""
}
})
.onClick(function (finished) {
if(finished) {
this.setAttribute("selected", "")
this.style.borderLeft = "1px solid var(--accent)"
this.style.paddingLeft = "9px"
.onClick((done, e) => {
if(done) {
this.toggleSelectedStyles(e.target)
window.navigateTo(`/${networks[i].abbreviation}`)
}
})
.cursor("default")
@@ -41,7 +65,7 @@ class Sidebar extends Shadow {
.borderRight("1px solid var(--accent)")
.zIndex(3)
.onEvent("themechange", () => {
console.log("change")
console.log("why is this needed smg")
this.rerender()
})
}

View File

@@ -14,6 +14,13 @@
}
}
</script>
<script type="importmap">
{
"imports": {
"fs": "/_/code/ws/shim/fs.js"
}
}
</script>
<script src="/_/code/quill.js"></script>
<script src="/_/code/zod.js"></script>
<script type="module" src="75820185/index.js"></script>

View File

@@ -1,118 +1,149 @@
import Socket from "/_/code/ws/Socket.js"
import "./Home.js"
import util from "./util.js"
window.util = util
window.Socket = new Socket()
let Global = class {
Socket = new Socket()
profile = null
currentNetwork = ""
currentApp = ""
util = util
window.currentNetwork = ""
window.currentApp = ""
async function openNetworkAndApp() {
if(window.currentNetwork !== networkFromPath()) {
window.currentNetwork = networkFromPath()
const event = new CustomEvent('networkchanged', {
detail: { name: currentNetwork }
});
window.dispatchEvent(event)
}
if(!window.currentNetwork.data) {
let appData = await fetch("/app/orgdata/" + window.profile.networks[0].id, {method: "GET"})
let json = await appData.json()
window.currentNetwork.data = json
}
if(window.currentApp !== appFromPath()) {
window.currentApp = appFromPath()
openApp = function(appName) {
const appUrl = appName.charAt(0).toLowerCase() + appName.slice(1);
let parts = window.location.pathname.split('/').filter(Boolean);
let newPath = "/" + parts[0] + "/" + appUrl
window.navigateTo(newPath)
const event = new CustomEvent('appchange', {
detail: { name: window.currentApp }
detail: { name: appName }
});
window.dispatchEvent(event)
}
if(window.currentNetwork) { // 2 navigates fire on load: 1 initial, and one after the org redirect
document.title = `${window.currentNetwork.abbreviation} | Parchment`
async fetchAppData() {
let personalSpace = this.currentNetwork === this.profile
let appData = await fetch(`/app/${personalSpace ? "my" : "org"}data/` + this.currentNetwork.id, {method: "GET"})
let json = await appData.json()
return json
}
}
window.addEventListener("navigate", openNetworkAndApp)
onNavigate = async () => {
window.openApp = function(appName) {
const appUrl = appName.charAt(0).toLowerCase() + appName.slice(1);
let parts = window.location.pathname.split('/').filter(Boolean);
let newPath = "/" + parts[0] + "/" + appUrl
window.navigateTo(newPath)
const event = new CustomEvent('appchange', {
detail: { name: appName }
});
window.dispatchEvent(event)
}
let selectedNetwork = this.networkFromPath()
let selectedApp = this.appFromPath()
window.networkFromPath = function () {
const pathname = window.location.pathname;
const firstSegment = pathname.split('/').filter(Boolean)[0] || '';
let networks = window.profile?.networks
for(let i = 0; i < networks.length; i++) {
let network = networks[i]
if(network.abbreviation === firstSegment) {
return network
if(!selectedNetwork) {
if(this.profile.networks.length > 0) {
let path = `/${this.getDefaultNetworkName()}/${this.getDefaultAppName()}`
history.replaceState({}, '', path)
}
} else if(!selectedApp) {
if(this.currentNetwork === window.profile) {
history.replaceState({}, '', `${window.location.pathname}/${window.profile.apps[0]}`)
} else {
history.replaceState({}, '', `${window.location.pathname}/${this.getDefaultAppName()}`)
}
}
selectedNetwork = this.networkFromPath()
selectedApp = this.appFromPath()
let networkChanged = this.currentNetwork !== selectedNetwork
let appChanged = this.currentApp !== selectedApp
if(networkChanged) {
this.currentNetwork = selectedNetwork
this.currentApp = selectedApp
const event = new CustomEvent('networkchange', {
detail: { name: this.currentNetwork }
});
window.dispatchEvent(event)
}
if(!this.currentNetwork.data) {
this.currentNetwork.data = await this.fetchAppData()
}
if(appChanged && !networkChanged) {
this.currentApp = selectedApp
const event = new CustomEvent('appchange', {
detail: { name: this.currentApp }
});
window.dispatchEvent(event)
}
document.title = (this.currentNetwork === this.profile) ? "Parchment" : `${this.currentNetwork.abbreviation} | Parchment`
}
setCurrentNetworkAndApp() {
this.currentNetwork = this.networkFromPath()
}
getDefaultNetworkName() {
let defaultNetwork = this.profile.networks[0]
return defaultNetwork.abbreviation
}
getDefaultAppName() {
let defaultNetwork = this.profile.networks[0]
return defaultNetwork.apps[0].toLowerCase()
}
networkFromPath = function () {
const pathname = window.location.pathname;
const firstSegment = pathname.split('/').filter(Boolean)[0] || '';
if(firstSegment === "my") {
return this.profile
} else {
let networks = this.profile.networks
for(let i = 0; i < networks.length; i++) {
let network = networks[i]
if(network.abbreviation === firstSegment) {
return network
}
}
}
}
}
window.appFromPath = function() {
const pathname = window.location.pathname;
const segments = pathname.split('/').filter(Boolean);
const secondSegment = segments[1] || "";
const capitalized = secondSegment.charAt(0).toUpperCase() + secondSegment.slice(1);
return capitalized
}
appFromPath = function() {
const pathname = window.location.pathname;
const segments = pathname.split('/').filter(Boolean);
const secondSegment = segments[1] || ""
const capitalized = secondSegment.charAt(0).toUpperCase() + secondSegment.slice(1);
return capitalized
}
async function getProfile() {
try {
const res = await fetch("/profile", {
method: "GET",
credentials: "include",
headers: {
"Content-Type": "application/json"
}
});
async getProfile() {
try {
const res = await fetch("/profile", {
method: "GET",
credentials: "include",
headers: {
"Content-Type": "application/json"
}
});
if (!res.ok) throw new Error("Failed to fetch profile");
if (!res.ok) throw new Error("Failed to fetch profile");
const profile = await res.json();
console.log("getProfile: ", profile);
window.profile = profile
} catch (err) {
console.error(err);
const profile = await res.json();
console.log("getProfile: ", profile);
this.profile = profile
} catch (err) {
console.error(err);
}
}
constructor() {
window.addEventListener("navigate", this.onNavigate)
this.getProfile().then(async () => {
await this.onNavigate()
Home()
})
}
}
function getInitialNetworkPath() {
let path = ""
let defaultNetwork = window.profile.networks[0]
if(!networkFromPath()) {
path += (defaultNetwork.abbreviation + "/")
}
if(!appFromPath()) {
let defaultApp = defaultNetwork.apps[0]
path += defaultApp.toLowerCase()
}
return path
}
getProfile().then(async () => {
if(window.profile.networks.length > 0) {
let path = getInitialNetworkPath()
window.navigateTo(path)
}
Home()
})
window.global = new Global()

View File

@@ -1,62 +0,0 @@
class Connection {
connectionTries = 0
ws;
linkCreated;
wsStatus;
constructor(receiveCB) {
this.init()
this.receiveCB = receiveCB
}
init() {
if(window.location.hostname.includes("local")) {
this.ws = new WebSocket("ws://" + window.location.host)
} else {
this.ws = new WebSocket("wss://" + window.location.hostname + window.location.pathname)
}
this.ws.addEventListener('open', () => {
this.connectionTries = 0
console.log("Websocket connection established.");
this.ws.addEventListener('message', this.receiveCB)
});
this.ws.addEventListener("close", () => {
this.checkOpen();
console.log('Websocket Closed')
})
}
async checkOpen() {
if (this.ws.readyState === WebSocket.OPEN) {
return true
} else {
await this.sleep(this.connectionTries < 20 ? 5000 : 60000)
this.connectionTries++
console.log('Reestablishing connection')
this.init()
}
}
sleep = (time) => {
return new Promise(resolve => {
setTimeout(resolve, time);
});
}
send = (msg) => {
console.log("sending")
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(msg);
}
else if(this.connectionTries === 0) {
setTimeout(() => {
this.send(msg)
}, 100)
}
else {
console.error('No websocket connection: Cannot send message');
}
}
}
export default Connection

View File

@@ -1,45 +0,0 @@
import Connection from "./Connection.js";
export default class Socket {
connection;
disabled = true;
requestID = 1;
pending = new Map();
constructor() {
this.connection = new Connection(this.receive);
}
isOpen() {
if(this.connection.checkOpen()) {
return true;
} else {
return false;
}
}
send(msg) {
return new Promise(resolve => {
const id = (++this.requestID).toString();
this.pending.set(id, resolve);
this.connection.send(JSON.stringify({ id, ...msg }));
});
}
receive = (event) => {
const msg = JSON.parse(event.data);
if (msg.id && this.pending.has(msg.id)) {
this.pending.get(msg.id)(msg);
this.pending.delete(msg.id);
return;
} else {
this.onBroadcast(msg)
}
}
onBroadcast(msg) {
window.dispatchEvent(new CustomEvent(msg.event, {
detail: msg.msg
}));
}
}

View File

@@ -8,8 +8,8 @@ class Home extends Shadow {
ZStack(() => {
ZStack(() => {
console.log(window.currentApp)
switch(window.currentApp) {
console.log(global.currentApp)
switch(global.currentApp) {
case "Dashboard":
Dashboard()
break;

View File

@@ -1,7 +1,7 @@
class Dashboard extends Shadow {
render() {
VStack(() => {
console.log(window.currentNetwork)
console.log(global.currentNetwork)
})
.width(100, pct)
.height(100, pct)

View File

@@ -4,45 +4,45 @@ import "./Home.js"
import util from "./util.js"
window.util = util
window.Socket = new Socket()
global.Socket = new Socket()
window.currentNetwork = ""
window.currentApp = ""
global.currentNetwork = ""
global.currentApp = ""
async function openNetworkAndApp() {
// console.log("currentApp: ", currentApp, "currentnet: ", currentNetwork, "nfrompath: ", networkFromPath(), "afrompath", appFromPath())
if(window.currentNetwork !== networkFromPath()) {
window.currentNetwork = networkFromPath()
const event = new CustomEvent('networkchanged', {
if(global.currentNetwork !== networkFromPath()) {
global.currentNetwork = networkFromPath()
const event = new CustomEvent('networkchange', {
detail: { name: currentNetwork }
});
window.dispatchEvent(event)
}
if(!window.currentNetwork.data) {
let appData = await fetch("/app/orgdata/" + window.profile.networks[0].id, {method: "GET"})
if(!global.currentNetwork.data) {
let appData = await fetch("/app/orgdata/" + global.profile.networks[0].id, {method: "GET"})
let json = await appData.json()
window.currentNetwork.data = json
global.currentNetwork.data = json
}
console.log("current: ", window.currentApp, "afrompath: ", appFromPath())
if(window.currentApp !== appFromPath()) {
window.currentApp = appFromPath()
console.log("current: ", global.currentApp, "afrompath: ", appFromPath())
if(global.currentApp !== appFromPath()) {
global.currentApp = appFromPath()
const event = new CustomEvent('appchange', {
detail: { name: window.currentApp }
detail: { name: global.currentApp }
});
window.dispatchEvent(event)
}
if(window.currentNetwork) { // 2 navigates fire on load: 1 initial, and one after the org redirect
document.title = `${window.currentNetwork.abbreviation} | Parchment`
if(global.currentNetwork) { // 2 navigates fire on load: 1 initial, and one after the org redirect
document.title = `${global.currentNetwork.abbreviation} | Parchment`
}
}
window.addEventListener("navigate", openNetworkAndApp)
window.openApp = function(appName) {
global.currentApp = function(appName) {
const appUrl = appName.charAt(0).toLowerCase() + appName.slice(1);
let parts = window.location.pathname.split('/').filter(Boolean);
let newPath = "/" + parts[0] + "/" + appUrl
@@ -56,7 +56,7 @@ window.openApp = function(appName) {
window.networkFromPath = function () {
const pathname = window.location.pathname;
const firstSegment = pathname.split('/').filter(Boolean)[0] || '';
let networks = window.profile?.networks
let networks = global.profile?.networks
for(let i = 0; i < networks.length; i++) {
let network = networks[i]
if(network.abbreviation === firstSegment) {
@@ -87,7 +87,7 @@ async function getProfile() {
const profile = await res.json();
console.log("getProfile: ", profile);
window.profile = profile
global.profile = profile
} catch (err) {
console.error(err);
}
@@ -95,7 +95,7 @@ async function getProfile() {
function getInitialNetworkPath() {
let path = ""
let defaultNetwork = window.profile.networks[0]
let defaultNetwork = global.profile.networks[0]
if(!networkFromPath()) {
path += (defaultNetwork.abbreviation + "/")
@@ -111,7 +111,7 @@ function getInitialNetworkPath() {
getProfile().then(async () => {
if(window.profile.networks.length > 0) {
if(global.profile.networks.length > 0) {
let path = getInitialNetworkPath()
window.navigateTo(path)
}

View File

@@ -35,10 +35,12 @@ class Home extends Shadow {
p("Parchment is a platform for small to medium-sized communities.")
.color("var(--quillred)")
.borderTop("1px solid var(--quillred)")
.verticalAlign("center")
.borderHorizontal("1px solid var(--quillred)")
.height(5, em)
.fontSize(window.isMobile() ? 1.5 : 1.2, vmax)
.fontSize(window.isMobile() ? 2 : 1.2, vmax)
.paddingTop(2, vmax)
.paddingBottom(window.isMobile() ? 1 : 0, em)
.paddingHorizontal(2, vmax)
})