Improve templates; Add navbar; Show book details
This commit is contained in:
parent
c0b154e1f6
commit
14c0dddf93
|
@ -17,7 +17,7 @@
|
|||
"filenamify": "^2.1.0",
|
||||
"helmet": "^3.15.0",
|
||||
"jquery": "^3.3.1",
|
||||
"slugify": "^1.3.4",
|
||||
"snarkdown": "^1.2.2",
|
||||
"socket.io": "^2.2.0",
|
||||
"socket.io-client": "^2.2.0",
|
||||
"unused-filename": "^1.0.0"
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
$(document).ready(function() {
|
||||
var socket = io();
|
||||
|
||||
$('.modal-background, .modal-close').click(function() {
|
||||
$(this).parent('.modal').removeClass('is-active');
|
||||
$('.modal-background, .modal-close, .modal-card-head .delete, .modal-card-foot .close').click(function() {
|
||||
$(this).closest('.modal').removeClass('is-active');
|
||||
});
|
||||
|
||||
$('.modal-button').click(function() {
|
||||
var modal = $(this).data('modal');
|
||||
$('#' + modal).addClass('is-active');
|
||||
});
|
||||
|
||||
$('#book').change(function() {
|
||||
|
@ -16,9 +21,4 @@ $(document).ready(function() {
|
|||
}
|
||||
$('#bookFileName').text(fileName ? fileName : 'None Selected');
|
||||
});
|
||||
|
||||
$('.book').click(function() {
|
||||
var modal = $(this).data('modal');
|
||||
$('#' + modal).addClass('is-active');
|
||||
});
|
||||
});
|
64
server.js
64
server.js
|
@ -8,6 +8,7 @@ const bodyParser = require('body-parser');
|
|||
const fileUpload = require('express-fileupload');
|
||||
const filenamify = require('filenamify');
|
||||
const unusedFilename = require('unused-filename');
|
||||
const snarkdown = require('snarkdown');
|
||||
|
||||
const settings = require('./settings.json');
|
||||
|
||||
|
@ -44,21 +45,7 @@ function Server () {
|
|||
this.server.use('/css', express.static(path.join(__dirname, './public/css/')));
|
||||
|
||||
this.server.get('/', (req, res) => {
|
||||
const files = fs.readdirSync(this.fileLocation).filter(fileName => fileName.includes('.json'));
|
||||
const books = files.map(fileName => {
|
||||
const bookData = JSON.parse(fs.readFileSync(path.resolve(this.fileLocation, fileName), 'utf8'));
|
||||
const id = fileName.replace('.json', '');
|
||||
const content = '<div class="box">' + bookData.summary + '</div>';
|
||||
const modal = this.fillTemplate('./templates/elements/modal.html', { content, id });
|
||||
return this.fillTemplate('./templates/elements/book.html', {
|
||||
id,
|
||||
title: bookData.title,
|
||||
author: bookData.author,
|
||||
modal,
|
||||
})
|
||||
}).join('');
|
||||
const body = this.fillTemplate('./templates/pages/booksList.html', { books });
|
||||
const html = this.fillTemplate('./templates/htmlContainer.html', { body });
|
||||
const html = this.generateHomePage();
|
||||
if (html) {
|
||||
res.send(html);
|
||||
} else {
|
||||
|
@ -66,14 +53,19 @@ function Server () {
|
|||
}
|
||||
});
|
||||
|
||||
this.server.get('/give', (req, res) => {
|
||||
const body = this.fillTemplate('./templates/pages/uploadForm.html');
|
||||
const html = this.fillTemplate('./templates/htmlContainer.html', { title: 'Give a Book', body });
|
||||
res.send(html);
|
||||
});
|
||||
this.server.post('/give', (req, res) => {
|
||||
if (Object.keys(req.files).length > 0
|
||||
&& req.body.hasOwnProperty('title') && req.body.title.trim() !== ''
|
||||
&& req.body.hasOwnProperty('summary') && req.body.summary.trim() !== '') {
|
||||
const { book } = req.files;
|
||||
const { title, author, summary } = req.body;
|
||||
const { title, author, summary, contributor } = req.body;
|
||||
const fileType = book.name.substr(book.name.lastIndexOf('.'));
|
||||
this.addBook({ book, title, author, summary, fileType }, () => {
|
||||
this.addBook({ book, title, author, summary, contributor, fileType }, () => {
|
||||
const messageBox = this.fillTemplate('./templates/elements/messageBox.html', {
|
||||
style: 'is-success',
|
||||
header: 'Upload Successful',
|
||||
|
@ -84,7 +76,7 @@ function Server () {
|
|||
content: messageBox,
|
||||
});
|
||||
const body = this.fillTemplate('./templates/pages/uploadForm.html');
|
||||
const html = this.fillTemplate('./templates/htmlContainer.html', { body, modal });
|
||||
const html = this.fillTemplate('./templates/htmlContainer.html', { title: 'Give a Book', body, modal });
|
||||
res.send(html);
|
||||
}, (err) => {
|
||||
const messageBox = this.fillTemplate('./templates/elements/messageBox.html', {
|
||||
|
@ -97,7 +89,7 @@ function Server () {
|
|||
content: messageBox,
|
||||
});
|
||||
const body = this.fillTemplate('./templates/pages/uploadForm.html');
|
||||
const html = this.fillTemplate('./templates/htmlContainer.html', { body, modal });
|
||||
const html = this.fillTemplate('./templates/htmlContainer.html', { title: 'Give a Book', body, modal });
|
||||
res.send(html);
|
||||
});
|
||||
} else {
|
||||
|
@ -117,7 +109,7 @@ function Server () {
|
|||
message: errorMessage,
|
||||
});
|
||||
const body = this.fillTemplate('./templates/pages/uploadForm.html');
|
||||
const html = this.fillTemplate('./templates/htmlContainer.html', { body, message });
|
||||
const html = this.fillTemplate('./templates/htmlContainer.html', { title: 'Give a Book', body, message });
|
||||
res.send(html);
|
||||
}
|
||||
});
|
||||
|
@ -130,7 +122,7 @@ function Server () {
|
|||
if (fileLocation) {
|
||||
console.log(socket.id + ' removed ' + bookId);
|
||||
const downloadLocation = fileLocation.substr(fileLocation.lastIndexOf('/'));
|
||||
socket.emit('./files' + downloadLocation);
|
||||
socket.emit('get book', './files' + downloadLocation);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -153,7 +145,9 @@ Server.prototype.fillTemplate = function (file, templateVars = {}) {
|
|||
this.templateCache[file] = data;
|
||||
}
|
||||
|
||||
let filledTemplate = data.replace(/\{\{allowedFormats\}\}/g, settings.allowedFormats.join(','))
|
||||
let filledTemplate = data.replace(/\{\{siteTitle\}\}/g, settings.siteTitle)
|
||||
.replace(/\{\{titleSeparator\}\}/g, settings.titleSeparator)
|
||||
.replace(/\{\{allowedFormats\}\}/g, settings.allowedFormats.join(','))
|
||||
.replace(/\{\{maxFileSize\}\}/g, (settings.maxFileSize > 0 ? settings.maxFileSize + 'MB' : 'no'));
|
||||
|
||||
for (let templateVar in templateVars) {
|
||||
|
@ -170,6 +164,31 @@ Server.prototype.fillTemplate = function (file, templateVars = {}) {
|
|||
return data;
|
||||
}
|
||||
|
||||
Server.prototype.generateHomePage = function () {
|
||||
const files = fs.readdirSync(this.fileLocation).filter(fileName => fileName.includes('.json'));
|
||||
const books = files.map(fileName => {
|
||||
const bookData = JSON.parse(fs.readFileSync(path.resolve(this.fileLocation, fileName), 'utf8'));
|
||||
const id = fileName.replace('.json', '');
|
||||
const header = '<h2 class="title">' + bookData.title + '</h2><h4 class="subtitle">' + bookData.author + '</h4>';
|
||||
const content = '<div class="content"><h4>Contributed by ' + bookData.contributor + '</h4>' + snarkdown(bookData.summary) + '</div>';
|
||||
const footer = '<a class="button close">Close</a> <a class="button is-success take-book">Take Book</a>';
|
||||
const modal = this.fillTemplate('./templates/elements/modalCard.html', {
|
||||
id,
|
||||
header,
|
||||
content,
|
||||
footer,
|
||||
});
|
||||
return this.fillTemplate('./templates/elements/book.html', {
|
||||
id,
|
||||
title: bookData.title,
|
||||
author: bookData.author,
|
||||
modal,
|
||||
});
|
||||
}).join('');
|
||||
const body = this.fillTemplate('./templates/pages/booksList.html', { books });
|
||||
return this.fillTemplate('./templates/htmlContainer.html', { title: 'View', body });
|
||||
}
|
||||
|
||||
Server.prototype.broadcastVisitors = function () {
|
||||
const numberConnected = this.io.of('/').clients().connected.length;
|
||||
this.io.emit('connected', numberConnected);
|
||||
|
@ -190,6 +209,7 @@ Server.prototype.addBook = function (uploadData = {}, success = () => {}, error
|
|||
title: uploadData.title.trim(),
|
||||
author: uploadData.author.trim(),
|
||||
summary: uploadData.summary.trim(),
|
||||
contributor: uploadData.contributor.trim(),
|
||||
fileType: book.name.substr(book.name.lastIndexOf('.')),
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
{
|
||||
"port": 3000,
|
||||
"siteTitle": "Little Library",
|
||||
"titleSeparator": " | ",
|
||||
"fileLocation": "./public/files/",
|
||||
"historyLocation": "./public/history/",
|
||||
"maxLibrarySize": 0,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<div class="column is-one-quarter">
|
||||
<div class="box book has-text-centered" data-modal="{{id}}">
|
||||
<div class="box modal-button has-text-centered" data-modal="{{id}}">
|
||||
<h2 class="title">{{title}}</h2>
|
||||
<h4 class="subtitle">{{author}}</h4>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<div class="modal {{isActive}}" id="{{id}}">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<div class="modal-card-title">{{header}}</div>
|
||||
<button class="delete" aria-label="close"></button>
|
||||
</header>
|
||||
<section class="modal-card-body">
|
||||
{{content}}
|
||||
</section>
|
||||
<footer class="modal-card-foot">
|
||||
{{footer}}
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
|
@ -6,7 +6,7 @@
|
|||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>{{title}}Little Library</title>
|
||||
<title>{{title}}{{titleSeparator}}{{siteTitle}}</title>
|
||||
<meta name="description" content="A digital give-a-book, take-a-book library for ebooks">
|
||||
|
||||
<link rel="stylesheet" href="./css/bulma.min.css?v=1.0">
|
||||
|
@ -17,9 +17,57 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
{{message}}
|
||||
<nav class="navbar" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="/">
|
||||
<h1 class="title">{{siteTitle}}</h1>
|
||||
</a>
|
||||
|
||||
<a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbarBasicExample" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<a class="navbar-item" href="/">
|
||||
View
|
||||
</a>
|
||||
|
||||
{{body}}
|
||||
<a class="navbar-item" href="/give">
|
||||
Give
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="navbar-end">
|
||||
<a class="navbar-item" href="/about">
|
||||
About
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
|
||||
{{message}}
|
||||
|
||||
{{body}}
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="content has-text-centered">
|
||||
<p>
|
||||
<strong>Little Library</strong> by <a href="https://robbie.antenesse.net">Robbie Antenesse</a>. The source code is licensed
|
||||
<a href="http://opensource.org/licenses/mit-license.php">MIT</a>. The website content
|
||||
is licensed <a href="http://creativecommons.org/licenses/by-nc-sa/4.0/">CC BY NC SA 4.0</a>.
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
{{modal}}
|
||||
<script src="./js/little-library.js"></script>
|
||||
|
|
|
@ -1,18 +1,28 @@
|
|||
<form action="./give" method="post" enctype="multipart/form-data">
|
||||
<div class="field">
|
||||
<label class="label">Book Title:
|
||||
<label class="label" for="title">Book Title:</label>
|
||||
<div class="control">
|
||||
<input type="text" class="input" name="title" id="title">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Book Author:
|
||||
<label class="label" for="author"><span class="is-italic has-text-weight-normal has-text-grey">(Optional)</span> Book Author:</label>
|
||||
<div class="control">
|
||||
<input type="text" class="input" name="author" id="author">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Why you're sharing it:<br>
|
||||
<label class="label" for="summary">Why you're sharing it:</label>
|
||||
<div class="control">
|
||||
<div class="help">Markdown Enabled</div>
|
||||
<textarea class="textarea" name="summary" id="summary"></textarea>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="contributor"><span class="is-italic has-text-weight-normal has-text-grey">(Optional)</span> Your name:</label>
|
||||
<div class="control">
|
||||
<input type="text" class="input" name="contributor" id="contributor">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
|
|
|
@ -690,10 +690,10 @@ setprototypeof@1.1.0:
|
|||
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
|
||||
integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==
|
||||
|
||||
slugify@^1.3.4:
|
||||
version "1.3.4"
|
||||
resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.3.4.tgz#78d2792d7222b55cd9fc81fa018df99af779efeb"
|
||||
integrity sha512-KP0ZYk5hJNBS8/eIjGkFDCzGQIoZ1mnfQRYS5WM3273z+fxGWXeN0fkwf2ebEweydv9tioZIHGZKoF21U07/nw==
|
||||
snarkdown@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/snarkdown/-/snarkdown-1.2.2.tgz#0cfe2f3012b804de120fc0c9f7791e869c59cc74"
|
||||
integrity sha1-DP4vMBK4BN4SD8DJ93kehpxZzHQ=
|
||||
|
||||
socket.io-adapter@~1.1.0:
|
||||
version "1.1.1"
|
||||
|
|
Loading…
Reference in New Issue