smaller images

Former-commit-id: dde678d5fa343d18dcf0c82ef7a07936c676bfea
This commit is contained in:
Ben
2022-04-19 21:42:34 +01:00
parent ed5279c7c9
commit 234c34f780
17 changed files with 358 additions and 222 deletions

View File

@@ -23,6 +23,7 @@
<script type="module" src="/components/navbar.mjs"></script>
<script type="module" src="/components/search.mjs"></script>
<script type="module" src="/components/basket.mjs"></script>
<script type="module" src="/components/basket-popout.mjs"></script>
<script type="module" src="/components/notificationbar.mjs"></script>
<script type="module" src="/components/storefront.mjs"></script>
<script type="module" src="/components/tag.mjs"></script>

View File

@@ -0,0 +1,39 @@
<html>
<head>
<title>LegoLog!</title>
<meta name="viewport">
<link rel="icon" type="image/svg+xml" href="/res/favicon.svg">
<link rel="stylesheet" type="text/css" href="../global.css">
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Londrina+Solid&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Josefin+Sans&display=swap" rel="stylesheet">
<!-- Auth0 - a library for authentication -->
<script src="https://cdn.auth0.com/js/auth0-spa-js/1.13/auth0-spa-js.production.js"></script>
<!-- Components used on this page - they must be included to work -->
<script type="module" src="/components/components.mjs"></script>
<script type="module" src="/components/navbar.mjs"></script>
<script type="module" src="/components/search.mjs"></script>
<script type="module" src="/components/basket.mjs"></script>
<script type="module" src="/components/basket-popout.mjs"></script>
<script type="module" src="/components/notificationbar.mjs"></script>
<script type="module" src="/components/storefront.mjs"></script>
<script type="module" src="/components/tag.mjs"></script>
<script type="module" src="/components/product-list.mjs"></script>
<script type="module" src="/components/super-compact-listing.mjs"></script>
<script type="module" src="/components/compact-listing.mjs"></script>
<script type="module" src="/components/product-listing.mjs"></script>
<script type="module" src="/index.mjs"></script>
</head>
<body>
<notificationbar-component></notificationbar-component>
<navbar-component></navbar-component>
<limited-margin>
<basket-component></basket-component>
</limited-margin>
</body>

View File

@@ -0,0 +1,228 @@
import { RegisterComponent, Component } from './components.mjs';
// Basket is stored locally only and is not persisted to the server.
// It is used to store the current basket and is used to calculate the total price of the basket.
// It is also used to store the current user's basket.
// The structure of the basket is in local storage and is as follows:
// {
// "basket": {
// "items": {
// "item1~modifier": { quantity, type },
// "item2": { quantity, type },
// ...
// },
// "total": total
// },
// }
let basketCallback = null;
// TODO: Does the localstorage have a problem with mutual exclusion?
// TODO: Should the basket be persisted to the server?
export function AddProductToBasket(product, type, amount, brickModifier = 'none') {
if (localStorage.getItem('basket') === null || !localStorage.getItem('basket')) {
localStorage.setItem('basket', JSON.stringify({
items: {},
total: 0,
}));
}
const basket = JSON.parse(localStorage.getItem('basket'));
if (type === 'brick') {
product += '~' + brickModifier;
}
if (basket.items[product]) {
basket.items[product].quantity += amount;
} else {
basket.items[product] = {
quantity: amount,
type,
};
}
basket.total += amount;
localStorage.setItem('basket', JSON.stringify(basket));
if (basketCallback) {
basketCallback();
}
}
export function RemoveProductFromBasket(product, type, amount, brickModifier = 'none') {
if (localStorage.getItem('basket') === null || !localStorage.getItem('basket')) {
return;
}
const basket = JSON.parse(localStorage.getItem('basket'));
if (type === 'brick') {
product += '~' + brickModifier;
}
if (basket.items[product] > amount) {
basket.items[product] -= amount;
} else {
delete basket.items[product];
}
basket.total -= amount;
localStorage.setItem('basket', JSON.stringify(basket));
if (basketCallback) {
basketCallback();
}
}
class BasketPopout extends Component {
static __IDENTIFY() { return 'basket-popout'; }
constructor() {
super(BasketPopout);
}
OnLocalBasketUpdate() {
const basket = localStorage.getItem('basket');
if (basket) {
try {
const basketJSON = JSON.parse(basket);
this.setState({
items: basketJSON.items,
total: basketJSON.total,
});
} catch (e) {
console.log(e);
}
} else {
this.setState({
items: {},
total: 0,
});
}
}
OnMount() {
this.OnLocalBasketUpdate(Object.bind(this));
basketCallback = this.OnLocalBasketUpdate.bind(this);
}
Render() {
return {
template: /* html */`
<span id="basket-wrapper">
<div class="basket">
<img id="basket-icon" class="menu-item" src="https://www.svgrepo.com/show/343743/cart.svg" width="50px" stroke="#222" stroke-width="2px" alt="">
<span id="basket-count" class="menu-item">{this.state.total}</span>
</div>
<div id="basket-popup" class="popup">
<div class="popup-header">
<span class="popup-title">Basket</span>
<span class="popup-close">&times;</span>
</div>
<div class="popup-content">
<div class="popup-content-item">
<span class="popup-content-item-title">Total</span>
<span class="popup-content-item-value">{this.state.total}</span>
</div>
<div class="popup-content-item">
<span class="popup-content-item-title">Items</span>
${Object.keys(this.state.items).map((key) => {
const item = this.state.items[key];
console.log(item)
return /* html */`
<div class="popup-content-item">
<span class="popup-content-item-title">${item.quantity}x ${key}</span>
<span class="popup-content-item-value">${item.type}</span>
</div>
`;
}).join('')}
</div>
</div>
<div class="popup-footer">
<button class="popup-footer-button">View Basket</button>
</div>
</div>
</span>
`,
style: `
#basket-wrapper {
flex-basis: 4%;
}
.basket {
display: flex;
justify-content: space-between;
padding-bottom: 2px;
}
.basket:hover {
opacity: 0.5;
}
#basket-icon {
padding-top: 2px;
cursor: pointer;
}
#basket-count {
padding-top: 9px;
font-size: 1em;
font-weight: 100;
cursor: pointer;
}
.popup {
display: none;
}
.show {
display: flex;
}
#basket-popup {
position: absolute;
background-color: #AB8FFF;
right: 0;
width: 200px;
height: 200px;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 100;
}
`,
};
}
OnRender() {
// set up basket
const basketToggler = this.root.querySelector('.basket');
basketToggler.addEventListener('click', () => {
const popup = this.root.querySelector('.popup');
popup.classList.toggle('show');
popup.addEventListener('click', (e) => {
if (e.target.classList.contains('popup-close')) {
popup.classList.remove('show');
}
});
// allow "click off to close"
// document.addEventListener('click', (e) => {
// if (!popup.contains(e.target)) {
// popup.classList.remove('show');
// }
// });
});
}
}
RegisterComponent(BasketPopout);

View File

@@ -1,83 +1,5 @@
import { RegisterComponent, Component } from './components.mjs';
// Basket is stored locally only and is not persisted to the server.
// It is used to store the current basket and is used to calculate the total price of the basket.
// It is also used to store the current user's basket.
// The structure of the basket is in local storage and is as follows:
// {
// "basket": {
// "items": {
// "item1~modifier": { quantity, type },
// "item2": { quantity, type },
// ...
// },
// "total": total
// },
// }
let basketCallback = null;
// TODO: Does the localstorage have a problem with mutual exclusion?
// TODO: Should the basket be persisted to the server?
export function AddProductToBasket(product, type, amount, brickModifier = 'none') {
if (localStorage.getItem('basket') === null || !localStorage.getItem('basket')) {
localStorage.setItem('basket', JSON.stringify({
items: {},
total: 0,
}));
}
const basket = JSON.parse(localStorage.getItem('basket'));
if (type === 'brick') {
product += '~' + brickModifier;
}
if (basket.items[product]) {
basket.items[product].quantity += amount;
} else {
basket.items[product] = {
quantity: amount,
type,
};
}
basket.total += amount;
localStorage.setItem('basket', JSON.stringify(basket));
if (basketCallback) {
basketCallback();
}
}
export function RemoveProductFromBasket(product, type, amount, brickModifier = 'none') {
if (localStorage.getItem('basket') === null || !localStorage.getItem('basket')) {
return;
}
const basket = JSON.parse(localStorage.getItem('basket'));
if (type === 'brick') {
product += '~' + brickModifier;
}
if (basket.items[product] > amount) {
basket.items[product] -= amount;
} else {
delete basket.items[product];
}
basket.total -= amount;
localStorage.setItem('basket', JSON.stringify(basket));
if (basketCallback) {
basketCallback();
}
}
class Basket extends Component {
static __IDENTIFY() { return 'basket'; }
@@ -85,133 +7,30 @@ class Basket extends Component {
super(Basket);
}
OnLocalBasketUpdate() {
const basket = localStorage.getItem('basket');
if (basket) {
try {
const basketJSON = JSON.parse(basket);
this.setState({
items: basketJSON.items,
total: basketJSON.total,
});
} catch (e) {
console.log(e);
}
} else {
this.setState({
items: {},
total: 0,
});
}
}
OnMount() {
this.OnLocalBasketUpdate(Object.bind(this));
basketCallback = this.OnLocalBasketUpdate.bind(this);
}
Render() {
return {
template: /* html */`
<span id="basket-wrapper">
<div class="basket">
<img id="basket-icon" class="menu-item" src="https://www.svgrepo.com/show/343743/cart.svg" width="50px" stroke="#222" stroke-width="2px" alt="">
<span id="basket-count" class="menu-item">{this.state.total}</span>
</div>
<div id="basket-popup" class="popup">
<div class="popup-header">
<span class="popup-title">Basket</span>
<span class="popup-close">&times;</span>
</div>
<div class="popup-content">
<div class="popup-content-item">
<span class="popup-content-item-title">Total</span>
<span class="popup-content-item-value">{this.state.total}</span>
</div>
<div class="popup-content-item">
<span class="popup-content-item-title">Items</span>
<span class="popup-content-item-value">{this.state.items}</span>
</div>
</div>
<div class="popup-footer">
<button class="popup-footer-button">View Basket</button>
</div
</div>
</span>
<span class="tag">{this.state.name}</span>
`,
style: `
#basket-wrapper {
flex-basis: 4%;
}
.basket {
display: flex;
justify-content: space-between;
padding-bottom: 2px;
}
.basket:hover {
opacity: 0.5;
}
#basket-icon {
padding-top: 2px;
.tag {
font-size: 0.8em;
padding: 0.2em 0.5em;
margin-right: 0.3em;
margin-top: 0.2em;
margin-bottom: 0.2em;
line-height: 1.3em;
font-weight: bold;
background-color: #F2CA52;
cursor: pointer;
}
#basket-count {
padding-top: 9px;
font-size: 1em;
font-weight: 100;
cursor: pointer;
}
.popup {
display: none;
}
.show {
display: flex;
}
#basket-popup {
position: absolute;
background-color: #AB8FFF;
right: 0;
width: 200px;
height: 200px;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 100;
}
`,
};
}
OnRender() {
// set up basket
const basketToggler = this.root.querySelector('.basket');
basketToggler.addEventListener('click', () => {
const popup = this.root.querySelector('.popup');
popup.classList.toggle('show');
popup.addEventListener('click', (e) => {
if (e.target.classList.contains('popup-close')) {
popup.classList.remove('show');
}
});
// allow "click off to close"
// document.addEventListener('click', (e) => {
// if (!popup.contains(e.target)) {
// popup.classList.remove('show');
// }
// });
this.root.addEventListener('click', () => {
this.root.classList.toggle('tag-selected');
});
}
}

View File

@@ -1,5 +1,5 @@
import { RegisterComponent, Component, SideLoad } from './components.mjs';
import { AddProductToBasket } from './basket.mjs';
import { AddProductToBasket } from './basket-popout.mjs';
class ProductListing extends Component {
static __IDENTIFY() { return 'product-listing'; }

View File

@@ -43,7 +43,7 @@
<img id="fave-icon" class="menu-item" src="https://www.svgrepo.com/show/25921/heart.svg" width="45px" stroke="#222" stroke-width="2px" alt="">
<basket-component></basket-component>
<basket-popout-component></basket-popout-component>
</ul>
</nav>
</div>

View File

@@ -1,4 +1,4 @@
<div class="notification-bar">
n<div class="notification-bar">
<div class="notification-bar-text">
{this.state.timePretty} - {this.state.title}
</div>

View File

@@ -23,6 +23,7 @@
<script type="module" src="/components/navbar.mjs"></script>
<script type="module" src="/components/search.mjs"></script>
<script type="module" src="/components/basket.mjs"></script>
<script type="module" src="/components/basket-popout.mjs"></script>
<script type="module" src="/components/notificationbar.mjs"></script>
<script type="module" src="/components/storefront.mjs"></script>
<script type="module" src="/components/tag.mjs"></script>

View File

@@ -23,6 +23,7 @@
<script type="module" src="/components/navbar.mjs"></script>
<script type="module" src="/components/search.mjs"></script>
<script type="module" src="/components/basket.mjs"></script>
<script type="module" src="/components/basket-popout.mjs"></script>
<script type="module" src="/components/notificationbar.mjs"></script>
<script type="module" src="/components/storefront.mjs"></script>
<script type="module" src="/components/tag.mjs"></script>

View File

@@ -19,6 +19,7 @@
<script type="module" src="/components/navbar.mjs"></script>
<script type="module" src="/components/search.mjs"></script>
<script type="module" src="/components/basket.mjs"></script>
<script type="module" src="/components/basket-popout.mjs"></script>
<script type="module" src="/components/notificationbar.mjs"></script>
<script type="module" src="/components/storefront.mjs"></script>
<script type="module" src="/components/tag.mjs"></script>

View File

@@ -23,6 +23,7 @@
<script type="module" src="/components/navbar.mjs"></script>
<script type="module" src="/components/search.mjs"></script>
<script type="module" src="/components/basket.mjs"></script>
<script type="module" src="/components/basket-popout.mjs"></script>
<script type="module" src="/components/notificationbar.mjs"></script>
<script type="module" src="/components/storefront.mjs"></script>
<script type="module" src="/components/tag.mjs"></script>

50
db/image-smallinator.js Normal file
View File

@@ -0,0 +1,50 @@
const fs = require('fs');
const sharp = require('sharp');
// itterate over every single png file in img recursively
const dir = './db/img';
const replaceDir = './db/image';
// https://stackoverflow.com/questions/5827612/node-js-fs-readdir-recursive-directory-search
const { promisify } = require('util');
const { resolve } = require('path');
const readdir = promisify(fs.readdir);
const stat = promisify(fs.stat);
async function getFiles(dir) {
const subdirs = await readdir(dir);
const files = await Promise.all(subdirs.map(async (subdir) => {
const res = resolve(dir, subdir);
return (await stat(res)).isDirectory() ? getFiles(res) : res;
}));
return files.reduce((a, f) => a.concat(f), []);
}
function itterate(files) {
for (let i = 0; i < files.length; i++) {
const file = files[i];
// if file is a png, compress using sharp
if (file.endsWith('.png')) {
console.log(`Compressing ${file}`);
const fileName = file.split('img').pop();
sharp(file)
.withMetadata()
.png({
quality: 50,
compression: 6,
})
.toFile(`${replaceDir}/${fileName}`, (err) => {
if (err) {
console.log(err);
return;
}
console.log(`Compressed ${file}`);
});
}
}
}
getFiles(dir)
.then(files => itterate(files))
.catch(e => console.error(e));

13
package-lock.json generated
View File

@@ -25,7 +25,8 @@
"pg": "^8.7.3",
"pg-format": "^1.0.4",
"pg-native": "^3.0.0",
"sharp": "^0.30.3"
"sharp": "^0.30.3",
"sql-escape": "^1.0.1"
},
"devDependencies": {
"eslint": "^8.9.0",
@@ -8722,6 +8723,11 @@
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"node_modules/sql-escape": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/sql-escape/-/sql-escape-1.0.1.tgz",
"integrity": "sha1-96BQdDxPL5G8SqhVLtEO0cIoTi8="
},
"node_modules/stack-utils": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz",
@@ -15854,6 +15860,11 @@
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"sql-escape": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/sql-escape/-/sql-escape-1.0.1.tgz",
"integrity": "sha1-96BQdDxPL5G8SqhVLtEO0cIoTi8="
},
"stack-utils": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz",

View File

@@ -42,7 +42,8 @@
"pg": "^8.7.3",
"pg-format": "^1.0.4",
"pg-native": "^3.0.0",
"sharp": "^0.30.3"
"sharp": "^0.30.3",
"sql-escape": "^1.0.1"
},
"devDependencies": {
"eslint": "^8.9.0",

View File

@@ -1,3 +1,4 @@
const escape = require('sql-escape');
// http://stackoverflow.com/questions/11919065/sort-an-array-by-the-levenshtein-distance-with-best-performance-in-javascript
function LevenshteinDistance(s, t) {
@@ -50,8 +51,10 @@ function LevenshteinDistance(s, t) {
return d[n][m];
}
// TODO: get this working properly
function SanatiseQuery(query) {
return query.replace(/[^a-zA-Z0-9 ]/g, '').toLowerCase();
return escape(query).toLowerCase().replace(/[()*]/g, '');
}
module.exports = {

View File

@@ -3,9 +3,6 @@ const Logger = require('./logger.js');
const Config = require('./config.js');
const Database = require('./database/database.js');
const decompress = require('decompress');
const decompressTargz = require('decompress-targz');
const fs = require('fs');
console.log('LegoLog Setting Up:tm:');
@@ -18,28 +15,14 @@ async function main() {
logToConsole: process.env.LOG_CONSOLE,
logFile: process.env.LOG_FILE,
});
Logger.Info('DECOMPRESSING - DO NOT CLOSE, THIS MAY TAKE A WHILE...');
Logger.Info('DECOMPRESSING - DO NOT CLOSE, THIS MAY TAKE A WHILE...');
// connect to database
await Database.Connect();
// unzip images ASYNC
decompress('db/img.tar.gz', 'db/', {
plugins: [
decompressTargz(),
],
}).then(() => {
console.log('Files decompressed');
});
const tableQuery = fs.readFileSync('./db/schema.sql').toString();
/* eslint-disable-next-line */
await new Promise(async (resolve, reject) => {
// run setup script to create schema
await db.query(tableQuery, [], (err, res) => {
await Database.query(tableQuery, [], (err, res) => {
if (err) {
Logger.Error(err);
resolve();
@@ -56,7 +39,7 @@ async function main() {
const dump = fs.readFileSync('./db/dump.sql').toString();
/* eslint-disable-next-line */
await new Promise(async (resolve, reject) => {
await db.query(dump, [], (err, res) => {
await Database.query(dump, [], (err, res) => {
if (err) {
Logger.Error(err);
resolve();
@@ -69,10 +52,7 @@ async function main() {
});
});
await db.destroy();
Logger.Info('DECOMPRESSING - DO NOT CLOSE, THIS MAY TAKE A WHILE...');
Logger.Info('DECOMPRESSING - DO NOT CLOSE, THIS MAY TAKE A WHILE...');
await Database.destroy();
}
main();