switching networks works, established server functions
This commit is contained in:
4
db/personal/1/db.json
Normal file
4
db/personal/1/db.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"nodes": {},
|
||||
"edges": {}
|
||||
}
|
||||
31
notes.js
31
notes.js
@@ -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()
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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" });
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
38
ui/_/code/bridge/bridge.js
Normal file
38
ui/_/code/bridge/bridge.js
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
11
ui/_/code/bridge/serverFunctions.js
Normal file
11
ui/_/code/bridge/serverFunctions.js
Normal 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)
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
1
ui/_/code/ws/shim/fs.js
Normal file
@@ -0,0 +1 @@
|
||||
export default {}
|
||||
@@ -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);
|
||||
|
||||
@@ -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 = ""
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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 = ""
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class Dashboard extends Shadow {
|
||||
render() {
|
||||
VStack(() => {
|
||||
console.log(window.currentNetwork)
|
||||
console.log(global.currentNetwork)
|
||||
})
|
||||
.width(100, pct)
|
||||
.height(100, pct)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user