1, 4: Fixing 404 after logged out, adding market entry
This commit is contained in:
@@ -14,16 +14,18 @@ export default class AuthHandler {
|
||||
}
|
||||
|
||||
isLoggedInUser(req, res) {
|
||||
const token = req.cookies.auth_token; // read cookie
|
||||
const token = req.cookies.auth_token;
|
||||
|
||||
if (!token) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return true
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||
req.user = decoded;
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import express from 'express';
|
||||
import cors from 'cors'
|
||||
import cookieParser from 'cookie-parser'
|
||||
import http from 'http'
|
||||
import fs from 'fs'
|
||||
import chalk from 'chalk'
|
||||
import moment from 'moment'
|
||||
import path from 'path';
|
||||
@@ -20,11 +21,13 @@ class Server {
|
||||
db;
|
||||
auth;
|
||||
UIPath = path.join(__dirname, '../ui')
|
||||
DBPath = path.join(__dirname, '../db')
|
||||
|
||||
registerRoutes(router) {
|
||||
// router.post('/api/location', handlers.updateLocation)
|
||||
router.post('/login', this.auth.login)
|
||||
router.get('/signout', this.auth.logout)
|
||||
router.get('/db/images/*', this.getUserImage)
|
||||
router.get('/*', this.get)
|
||||
return router
|
||||
}
|
||||
@@ -49,24 +52,49 @@ class Server {
|
||||
}
|
||||
}
|
||||
|
||||
getUserImage = async (req, res) => {
|
||||
function getFileByNumber(dir, number) {
|
||||
const files = fs.readdirSync(dir);
|
||||
const match = files.find(file => {
|
||||
const base = path.parse(file).name; // filename without extension
|
||||
return base === String(number);
|
||||
});
|
||||
return match ? path.join(dir, match) : null;
|
||||
}
|
||||
let filePath = getFileByNumber(path.join(this.DBPath, "images"), path.basename(req.url))
|
||||
|
||||
res.sendFile(filePath)
|
||||
}
|
||||
|
||||
get = async (req, res) => {
|
||||
if(!this.auth.isLoggedInUser(req, res)) {
|
||||
console.log("Not logged in")
|
||||
let url = req.url
|
||||
if(url === "/") {
|
||||
url = "/index.html"
|
||||
} else if(!url.includes(".")) { // TODO: Make public app single-page
|
||||
url = path.join("/pages", url) + ".html"
|
||||
}
|
||||
|
||||
let filePath;
|
||||
if(url.startsWith("/_")) {
|
||||
filePath = path.join(this.UIPath, url);
|
||||
} else {
|
||||
filePath = path.join(this.UIPath, "public", url);
|
||||
}
|
||||
if(!url.includes(".")) { // Page request
|
||||
if(url === "/") {
|
||||
url = "/index.html"
|
||||
} else {
|
||||
url = path.join("/pages", url) + ".html"
|
||||
}
|
||||
|
||||
res.sendFile(filePath);
|
||||
let filePath = path.join(this.UIPath, "public", url);
|
||||
res.sendFile(filePath, (err) => {
|
||||
if (err) {
|
||||
console.log("File not found, sending fallback:", filePath);
|
||||
res.redirect("/");
|
||||
}
|
||||
});
|
||||
} else { // File Request
|
||||
let filePath;
|
||||
if(url.startsWith("/_")) {
|
||||
filePath = path.join(this.UIPath, url);
|
||||
} else {
|
||||
filePath = path.join(this.UIPath, "public", url);
|
||||
}
|
||||
|
||||
res.sendFile(filePath);
|
||||
}
|
||||
} else {
|
||||
let url = req.url
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Sam Russell
|
||||
Captured Sun
|
||||
11.9.25 - changed p(innerText) to p(innerHTML), adjusted onNavigate to work for multiple elements and with correct "this" scope
|
||||
11.7.25 - changed registerShadow() to register(), changed onClick() to be like onHover()
|
||||
11.6.25 - adding default value for "button()" "children" parameter
|
||||
10.29.25 - adding "gap()" and "label()" functions
|
||||
@@ -562,12 +563,12 @@ HTMLImageElement.prototype.backgroundColor = function(value) {
|
||||
return this; // Always returns the element itself
|
||||
};
|
||||
|
||||
window.p = function p(innerText) {
|
||||
window.p = function p(innerHTML) {
|
||||
let el = document.createElement("p")
|
||||
if(typeof innerText === "function") {
|
||||
el.render = innerText
|
||||
el.render = innerHTML
|
||||
} else {
|
||||
el.innerText = innerText
|
||||
el.innerHTML = innerHTML
|
||||
}
|
||||
el.style.margin = "0";
|
||||
quill.render(el)
|
||||
@@ -765,13 +766,15 @@ HTMLElement.prototype.onKeyDown = function(cb) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/* QUIRK 1:
|
||||
In all the other callback functions, the user can choose the scope of "this". It can be either the parent shadow or the element itself.
|
||||
This listener only allows for the latter functionality. This is because the navigate event fires on the window.
|
||||
Without binding, "this" would refer only to the window. So here we are compromising on one of the two.
|
||||
/* WHY THIS LISTENER IS THE WAY IT IS:
|
||||
- If we dispatch the "navigate" event on the window (as one would expect for a "navigate" event), a listener placed on the element will not pick it up.
|
||||
- However, if we add the event as a window event, it won't have the "this" scope that a callback normally would.
|
||||
- Then, if we try to add that scope using bind(), it makes the function.toString() unreadable, which means we will get false positives for duplicate listeners.
|
||||
- Therefore, we just have to attach the navigate event to the element, and manually trigger that when the window listener fires.
|
||||
*/
|
||||
HTMLElement.prototype.onNavigate = function(cb) {
|
||||
window._storeListener(window, "navigate", cb.bind(this));
|
||||
this._storeListener("navigate", cb);
|
||||
window.addEventListener("navigate", () => this.dispatchEvent(new CustomEvent("navigate")))
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<title>Hyperia</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" href="_/icons/logo.svg">
|
||||
<link rel="icon" href="/_/icons/logo.svg">
|
||||
<link rel="stylesheet" href="_/code/shared.css">
|
||||
<style>
|
||||
|
||||
|
||||
178
ui/site/apps/Forum/Forum.js
Normal file
178
ui/site/apps/Forum/Forum.js
Normal file
@@ -0,0 +1,178 @@
|
||||
css(`
|
||||
messages- {
|
||||
font-family: 'Bona';
|
||||
}
|
||||
|
||||
messages- input::placeholder {
|
||||
font-family: 'Bona Nova';
|
||||
font-size: 0.9em;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
appearance: none; /* remove default style */
|
||||
-webkit-appearance: none;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border: 1px solid var(--accent);
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked {
|
||||
background-color: var(--red);
|
||||
}
|
||||
`)
|
||||
|
||||
class Messages extends Shadow {
|
||||
friends = []
|
||||
conversations = []
|
||||
|
||||
render() {
|
||||
ZStack(() => {
|
||||
HStack(() => {
|
||||
VStack(() => {
|
||||
h3("Friends")
|
||||
.marginTop(0)
|
||||
.marginBottom(1, em)
|
||||
.marginLeft(0.4, em)
|
||||
|
||||
if (this.friends.length > 1) {
|
||||
for(let i = 0; i < this.friends.length; i++) {
|
||||
p(this.friends[i].name)
|
||||
}
|
||||
} else {
|
||||
p("No Friends!")
|
||||
}
|
||||
})
|
||||
.height(100, vh)
|
||||
.paddingLeft(2, em)
|
||||
.paddingRight(2, em)
|
||||
.paddingTop(2, em)
|
||||
.gap(0, em)
|
||||
.borderRight("1px solid var(--periwinkle)")
|
||||
|
||||
VStack(() => {
|
||||
h3("Conversations")
|
||||
.marginTop(0)
|
||||
.marginBottom(1, em)
|
||||
.marginLeft(0.4, em)
|
||||
|
||||
if (this.conversations.length > 1) {
|
||||
for(let i = 0; i < this.conversations.length; i++) {
|
||||
p(this.conversations[i].name)
|
||||
}
|
||||
} else {
|
||||
p("No Conversations!")
|
||||
}
|
||||
})
|
||||
.height(100, vh)
|
||||
.paddingLeft(2, em)
|
||||
.paddingRight(2, em)
|
||||
.paddingTop(2, em)
|
||||
.gap(0, em)
|
||||
.borderRight("1px solid var(--periwinkle)")
|
||||
})
|
||||
.width(100, "%")
|
||||
.x(0).y(13, vh)
|
||||
.borderTop("1px solid var(--periwinkle)")
|
||||
|
||||
p("0 Items")
|
||||
.position("absolute")
|
||||
.x(50, vw).y(50, vh)
|
||||
.transform("translate(-50%, -50%)")
|
||||
|
||||
HStack(() => {
|
||||
input("Search messages...", "45vw")
|
||||
.attr({
|
||||
"type": "text"
|
||||
})
|
||||
.fontSize(1.1, em)
|
||||
.paddingLeft(1.3, em)
|
||||
.background("transparent")
|
||||
.border("1px solid var(--periwinkle)")
|
||||
.outline("none")
|
||||
.color("var(--accent)")
|
||||
.borderRadius(10, px)
|
||||
|
||||
button("Search")
|
||||
.marginLeft(2, em)
|
||||
.borderRadius(10, px)
|
||||
.background("transparent")
|
||||
.border("1px solid var(--periwinkle)")
|
||||
.color("var(--accent)")
|
||||
.fontFamily("Bona Nova")
|
||||
.onHover(function (hovering) {
|
||||
if(hovering) {
|
||||
this.style.background = "var(--green)"
|
||||
|
||||
} else {
|
||||
this.style.background = "transparent"
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
button("+ New Message")
|
||||
.width(13, em)
|
||||
.marginLeft(1, em)
|
||||
.borderRadius(10, px)
|
||||
.background("transparent")
|
||||
.border("1px solid var(--periwinkle)")
|
||||
.color("var(--accent)")
|
||||
.fontFamily("Bona Nova")
|
||||
.onHover(function (hovering) {
|
||||
if(hovering) {
|
||||
this.style.background = "var(--green)"
|
||||
|
||||
} else {
|
||||
this.style.background = "transparent"
|
||||
|
||||
}
|
||||
})
|
||||
.onClick((clicking) => {
|
||||
console.log(this, "clicked")
|
||||
})
|
||||
|
||||
})
|
||||
.x(55, vw).y(4, vh)
|
||||
.position("absolute")
|
||||
.transform("translateX(-50%)")
|
||||
})
|
||||
.width(100, "%")
|
||||
.height(100, "%")
|
||||
}
|
||||
|
||||
SidebarName(name) {
|
||||
let firstLetter = name[0]
|
||||
|
||||
HStack(() => {
|
||||
div(firstLetter)
|
||||
.display("flex")
|
||||
.justifyContent("center")
|
||||
.alignItems("center")
|
||||
.width(1.5, em)
|
||||
.height(1.5, em)
|
||||
.border("1px solid var(--periwinkle)")
|
||||
.borderRadius(100, "%")
|
||||
p(name)
|
||||
.marginLeft(1, em)
|
||||
})
|
||||
.alignItems("center")
|
||||
.padding(5, px)
|
||||
.borderRadius(0.5, em)
|
||||
.cursor("default")
|
||||
.onHover(function (hovering) {
|
||||
if(hovering) {
|
||||
this.style.background = "var(--green)"
|
||||
|
||||
} else {
|
||||
this.style.background = "transparent"
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
// Optional additional logic
|
||||
}
|
||||
}
|
||||
|
||||
register(Messages)
|
||||
@@ -29,12 +29,17 @@ class Jobs extends Shadow {
|
||||
jobs = [
|
||||
{
|
||||
title: "Austin Chapter Lead",
|
||||
salary: "1% of Local Tax Revenue",
|
||||
location: "Austin"
|
||||
salary: "1% of Local Revenue",
|
||||
company: "Hyperia",
|
||||
city: "Austin",
|
||||
state: "TX"
|
||||
},
|
||||
{
|
||||
title: "San Marcos Chapter Lead",
|
||||
salary: "1% of Local Tax Revenue"
|
||||
salary: "1% of Local Revenue",
|
||||
company: "Hyperia",
|
||||
city: "San Marcos",
|
||||
state: "TX"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -6,10 +6,19 @@ class JobsGrid extends Shadow {
|
||||
this.jobs = jobs
|
||||
}
|
||||
|
||||
boldUntilFirstSpace(text) {
|
||||
const index = text.indexOf(' ');
|
||||
if (index === -1) {
|
||||
// No spaces — bold the whole thing
|
||||
return `<b>${text}</b>`;
|
||||
}
|
||||
return `<b>${text.slice(0, index)}</b>${text.slice(index)}`;
|
||||
}
|
||||
|
||||
render() {
|
||||
VStack(() => {
|
||||
h3("Results")
|
||||
.marginTop(0)
|
||||
.marginTop(0.1, em)
|
||||
.marginBottom(1, em)
|
||||
.marginLeft(0.4, em)
|
||||
.color("var(--periwinkle)")
|
||||
@@ -17,14 +26,23 @@ class JobsGrid extends Shadow {
|
||||
if (this.jobs.length > 0) {
|
||||
ZStack(() => {
|
||||
for (let i = 0; i < this.jobs.length; i++) {
|
||||
p(this.jobs[i].title)
|
||||
.border("1px solid var(--periwinkle)")
|
||||
.padding(1, em)
|
||||
.borderRadius(5, "px")
|
||||
}
|
||||
VStack(() => {
|
||||
p(this.jobs[i].title)
|
||||
.fontSize(1.2, em)
|
||||
.fontWeight("bold")
|
||||
.marginBottom(0.5, em)
|
||||
p(this.jobs[i].company)
|
||||
p(this.jobs[i].city + ", " + this.jobs[i].state)
|
||||
.marginBottom(0.5, em)
|
||||
p(this.boldUntilFirstSpace(this.jobs[i].salary))
|
||||
})
|
||||
.padding(1, em)
|
||||
.border("1px solid var(--periwinkle)")
|
||||
.borderRadius(5, "px")
|
||||
}
|
||||
})
|
||||
.display("grid")
|
||||
.gridTemplateColumns("repeat(auto-fill, minmax(200px, 1fr))")
|
||||
.gridTemplateColumns("repeat(auto-fill, minmax(250px, 1fr))")
|
||||
.gap(1, em)
|
||||
} else {
|
||||
p("No Jobs!")
|
||||
|
||||
@@ -2,6 +2,7 @@ class JobsSidebar extends Shadow {
|
||||
render() {
|
||||
VStack(() => {
|
||||
h3("Location")
|
||||
.color("var(--periwinkle)")
|
||||
|
||||
|
||||
})
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import "./MarketSidebar.js"
|
||||
import "./MarketGrid.js"
|
||||
|
||||
css(`
|
||||
market- {
|
||||
font-family: 'Bona';
|
||||
@@ -24,73 +27,27 @@ css(`
|
||||
|
||||
class Market extends Shadow {
|
||||
|
||||
listings = [
|
||||
{
|
||||
title: "Shield Lapel Pin",
|
||||
stars: "5",
|
||||
reviews: 1,
|
||||
price: "$12",
|
||||
company: "Hyperia",
|
||||
type: "new",
|
||||
image: "/db/images/1"
|
||||
}
|
||||
]
|
||||
|
||||
render() {
|
||||
ZStack(() => {
|
||||
HStack(() => {
|
||||
VStack(() => {
|
||||
MarketSidebar()
|
||||
|
||||
HStack(() => {
|
||||
input()
|
||||
.attr({
|
||||
"type": "checkbox",
|
||||
"id": "hyperia-check"
|
||||
})
|
||||
label("Hyperia-Made")
|
||||
.attr({
|
||||
"for": "hyperia-check"
|
||||
})
|
||||
.marginLeft(0.5, em)
|
||||
})
|
||||
|
||||
HStack(() => {
|
||||
input()
|
||||
.attr({
|
||||
"type": "checkbox",
|
||||
"id": "america-check"
|
||||
})
|
||||
label("America-Made")
|
||||
.attr({
|
||||
"for": "america-check"
|
||||
})
|
||||
.marginLeft(0.5, em)
|
||||
})
|
||||
|
||||
HStack(() => {
|
||||
input()
|
||||
.attr({
|
||||
"type": "checkbox",
|
||||
"id": "new-check"
|
||||
})
|
||||
label("New")
|
||||
.attr({
|
||||
"for": "new-check"
|
||||
})
|
||||
.marginLeft(0.5, em)
|
||||
})
|
||||
|
||||
HStack(() => {
|
||||
input()
|
||||
.attr({
|
||||
"type": "checkbox",
|
||||
"id": "used-check"
|
||||
})
|
||||
label("Used")
|
||||
.attr({
|
||||
"for": "used-check"
|
||||
})
|
||||
.marginLeft(0.5, em)
|
||||
})
|
||||
})
|
||||
.paddingLeft(3, em)
|
||||
.gap(1, em)
|
||||
MarketGrid(this.listings)
|
||||
})
|
||||
.width(100, "%")
|
||||
.x(0).y(25, vh)
|
||||
|
||||
p("0 Items")
|
||||
.position("absolute")
|
||||
.x(50, vw).y(50, vh)
|
||||
.transform("translate(-50%, -50%)")
|
||||
.x(0).y(13, vh)
|
||||
|
||||
HStack(() => {
|
||||
input("Search for products...", "45vw")
|
||||
100
ui/site/apps/Market/MarketGrid.js
Normal file
100
ui/site/apps/Market/MarketGrid.js
Normal file
@@ -0,0 +1,100 @@
|
||||
class MarketGrid extends Shadow {
|
||||
listings;
|
||||
|
||||
constructor(listings) {
|
||||
super()
|
||||
this.listings = listings
|
||||
}
|
||||
|
||||
boldUntilFirstSpace(text) {
|
||||
if(!text) return
|
||||
const index = text.indexOf(' ');
|
||||
if (index === -1) {
|
||||
// No spaces — bold the whole thing
|
||||
return `<b>${text}</b>`;
|
||||
}
|
||||
return `<b>${text.slice(0, index)}</b>${text.slice(index)}`;
|
||||
}
|
||||
|
||||
render() {
|
||||
VStack(() => {
|
||||
h3("Results")
|
||||
.marginTop(0.1, em)
|
||||
.marginBottom(1, em)
|
||||
.marginLeft(0.4, em)
|
||||
.color("var(--periwinkle)")
|
||||
|
||||
if (this.listings.length > 0) {
|
||||
ZStack(() => {
|
||||
for (let i = 0; i < this.listings.length; i++) {
|
||||
const rating = this.listings[i].stars
|
||||
const percent = (rating / 5)
|
||||
|
||||
VStack(() => {
|
||||
img(this.listings[i].image)
|
||||
.marginBottom(0.5, em)
|
||||
|
||||
p(this.listings[i].company)
|
||||
.marginBottom(0.5, em)
|
||||
|
||||
p(this.listings[i].title)
|
||||
.fontSize(1.2, em)
|
||||
.fontWeight("bold")
|
||||
.marginBottom(0.5, em)
|
||||
|
||||
HStack(() => {
|
||||
p(this.listings[i].stars)
|
||||
.marginRight(0.2, em)
|
||||
|
||||
ZStack(() => {
|
||||
div("★★★★★") // Empty stars (background)
|
||||
.color("#ccc")
|
||||
|
||||
div("★★★★★") // Filled stars (foreground, clipped by width)
|
||||
.color("#ffa500")
|
||||
.position("absolute")
|
||||
.top(0)
|
||||
.left(0)
|
||||
.whiteSpace("nowrap")
|
||||
.overflow("hidden")
|
||||
.width(percent * 5, em)
|
||||
})
|
||||
.display("inline-block")
|
||||
.position("relative")
|
||||
.fontSize(1.2, em)
|
||||
.lineHeight(1)
|
||||
|
||||
p(this.listings[i].reviews)
|
||||
.marginLeft(0.2, em)
|
||||
})
|
||||
.marginBottom(0.5, em)
|
||||
|
||||
p(this.listings[i].price)
|
||||
.fontSize(1.75, em)
|
||||
.marginBottom(0.5, em)
|
||||
|
||||
button("Buy Now")
|
||||
|
||||
})
|
||||
.padding(1, em)
|
||||
.border("1px solid var(--periwinkle)")
|
||||
.borderRadius(5, "px")
|
||||
}
|
||||
})
|
||||
.display("grid")
|
||||
.gridTemplateColumns("repeat(auto-fill, minmax(250px, 1fr))")
|
||||
.gap(1, em)
|
||||
} else {
|
||||
p("No Listings!")
|
||||
}
|
||||
})
|
||||
.height(100, vh)
|
||||
.paddingLeft(2, em)
|
||||
.paddingRight(2, em)
|
||||
.paddingTop(2, em)
|
||||
.gap(0, em)
|
||||
.width(100, "%")
|
||||
}
|
||||
}
|
||||
|
||||
register(MarketGrid)
|
||||
65
ui/site/apps/Market/MarketSidebar.js
Normal file
65
ui/site/apps/Market/MarketSidebar.js
Normal file
@@ -0,0 +1,65 @@
|
||||
class MarketSidebar extends Shadow {
|
||||
render() {
|
||||
VStack(() => {
|
||||
HStack(() => {
|
||||
input()
|
||||
.attr({
|
||||
"type": "checkbox",
|
||||
"id": "hyperia-check"
|
||||
})
|
||||
label("Hyperia-Made")
|
||||
.attr({
|
||||
"for": "hyperia-check"
|
||||
})
|
||||
.marginLeft(0.5, em)
|
||||
})
|
||||
|
||||
HStack(() => {
|
||||
input()
|
||||
.attr({
|
||||
"type": "checkbox",
|
||||
"id": "america-check"
|
||||
})
|
||||
label("America-Made")
|
||||
.attr({
|
||||
"for": "america-check"
|
||||
})
|
||||
.marginLeft(0.5, em)
|
||||
})
|
||||
|
||||
HStack(() => {
|
||||
input()
|
||||
.attr({
|
||||
"type": "checkbox",
|
||||
"id": "new-check"
|
||||
})
|
||||
label("New")
|
||||
.attr({
|
||||
"for": "new-check"
|
||||
})
|
||||
.marginLeft(0.5, em)
|
||||
})
|
||||
|
||||
HStack(() => {
|
||||
input()
|
||||
.attr({
|
||||
"type": "checkbox",
|
||||
"id": "used-check"
|
||||
})
|
||||
label("Used")
|
||||
.attr({
|
||||
"for": "used-check"
|
||||
})
|
||||
.marginLeft(0.5, em)
|
||||
})
|
||||
})
|
||||
.paddingTop(12, vh)
|
||||
.paddingLeft(3, em)
|
||||
.paddingRight(3, em)
|
||||
.gap(1, em)
|
||||
.minWidth(10, vw)
|
||||
.userSelect('none')
|
||||
}
|
||||
}
|
||||
|
||||
register(MarketSidebar)
|
||||
@@ -111,7 +111,7 @@ class Messages extends Shadow {
|
||||
})
|
||||
|
||||
button("+ New Message")
|
||||
.width(15, em)
|
||||
.width(13, em)
|
||||
.marginLeft(1, em)
|
||||
.borderRadius(10, px)
|
||||
.background("transparent")
|
||||
|
||||
@@ -64,12 +64,44 @@ class AppMenu extends Shadow {
|
||||
.gap(1.5, em)
|
||||
.paddingRight(2, em)
|
||||
|
||||
img("_/images/divider.svg", "40vw")
|
||||
img("/_/images/divider.svg", "40vw")
|
||||
.attr({
|
||||
"id": "divider",
|
||||
})
|
||||
})
|
||||
.gap(0.5, em)
|
||||
.onNavigate(() => {
|
||||
if(window.location.pathname === "/") {
|
||||
this.styleMaximized()
|
||||
$("app-window").close()
|
||||
} else {
|
||||
this.styleMinimized()
|
||||
$("app-window").open(this.selected)
|
||||
}
|
||||
})
|
||||
.onAppear(() => {
|
||||
Array.from(this.querySelectorAll("p")).forEach((el) => {
|
||||
el.addEventListener("mousedown", (e) => {
|
||||
el.classList.add("touched")
|
||||
})
|
||||
})
|
||||
window.addEventListener("mouseup", (e) => {
|
||||
let target = e.target
|
||||
if(!target.matches("app-menu p")) {
|
||||
return
|
||||
}
|
||||
|
||||
target.classList.remove("touched")
|
||||
|
||||
if(target.classList.contains("selected")) {
|
||||
this.selected = ""
|
||||
window.navigateTo("/")
|
||||
} else {
|
||||
this.selected = target.innerText
|
||||
window.navigateTo("/app/" + target.innerText.toLowerCase())
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
if(this.selected) {
|
||||
this.styleMinimized()
|
||||
@@ -95,35 +127,6 @@ class AppMenu extends Shadow {
|
||||
this.classList.add("minimized")
|
||||
$("#divider").style.display = "none"
|
||||
}
|
||||
|
||||
|
||||
connectedCallback() {
|
||||
Array.from(this.querySelectorAll("p")).forEach((el) => {
|
||||
el.addEventListener("mousedown", (e) => {
|
||||
el.classList.add("touched")
|
||||
})
|
||||
})
|
||||
window.addEventListener("mouseup", (e) => {
|
||||
let target = e.target
|
||||
if(!target.matches("app-menu p")) {
|
||||
return
|
||||
}
|
||||
|
||||
target.classList.remove("touched")
|
||||
|
||||
if(target.classList.contains("selected")) {
|
||||
this.selected = ""
|
||||
this.styleMaximized(target)
|
||||
window.navigateTo("/")
|
||||
$("app-window").close()
|
||||
} else {
|
||||
this.selected = target.innerText
|
||||
this.styleMinimized(target)
|
||||
window.navigateTo("/app/" + target.innerText.toLowerCase())
|
||||
$("app-window").open(target.innerText)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
, "app-menu")
|
||||
@@ -1,6 +1,6 @@
|
||||
import "../apps/Jobs/Jobs.js"
|
||||
import "../apps/Messages.js"
|
||||
import "../apps/Market.js"
|
||||
import "../apps/Market/Market.js"
|
||||
|
||||
class AppWindow extends Shadow {
|
||||
app;
|
||||
|
||||
@@ -8,21 +8,20 @@ class Home extends Shadow {
|
||||
|
||||
render() {
|
||||
ZStack(() => {
|
||||
img("_/icons/logo.svg", "2.5em")
|
||||
img("/_/icons/logo.svg", "2.5em")
|
||||
.position("fixed")
|
||||
.left("3em")
|
||||
.top("3vh")
|
||||
.zIndex(3)
|
||||
// .onClick(() => {
|
||||
// window.navigateTo("/")
|
||||
// this.rerender()
|
||||
// })
|
||||
.onClick(() => {
|
||||
window.navigateTo("/")
|
||||
})
|
||||
|
||||
div()
|
||||
.width(100, vw)
|
||||
.height(100, vh)
|
||||
.margin("0px")
|
||||
.backgroundImage("url('_/images/the_return.webp')")
|
||||
.backgroundImage("url('/_/images/the_return.webp')")
|
||||
.backgroundSize("cover")
|
||||
.backgroundPosition("48% 65%")
|
||||
.backgroundRepeat("no-repeat")
|
||||
@@ -69,7 +68,6 @@ class Home extends Shadow {
|
||||
}
|
||||
})
|
||||
.onNavigate(function () {
|
||||
console.log("navigate")
|
||||
if(window.location.pathname === "/") {
|
||||
this.style.border = "1px solid var(--tan)"
|
||||
this.style.color = "var(--tan)"
|
||||
|
||||
Reference in New Issue
Block a user