Add History page

This commit is contained in:
Robbie Antenesse 2018-12-27 14:54:11 -07:00
parent b502470279
commit d7e77dc4dc
8 changed files with 111 additions and 32 deletions

View File

@ -14,6 +14,7 @@
"bulma": "^0.7.2", "bulma": "^0.7.2",
"express": "^4.16.4", "express": "^4.16.4",
"express-fileupload": "^1.0.0", "express-fileupload": "^1.0.0",
"fecha": "^3.0.2",
"filenamify": "^2.1.0", "filenamify": "^2.1.0",
"helmet": "^3.15.0", "helmet": "^3.15.0",
"jquery": "^3.3.1", "jquery": "^3.3.1",

View File

@ -9,6 +9,7 @@ const fileUpload = require('express-fileupload');
const filenamify = require('filenamify'); const filenamify = require('filenamify');
const unusedFilename = require('unused-filename'); const unusedFilename = require('unused-filename');
const snarkdown = require('snarkdown'); const snarkdown = require('snarkdown');
const fecha = require('fecha');
const settings = require('./settings.json'); const settings = require('./settings.json');
@ -37,12 +38,11 @@ function Server () {
})); }));
this.server.use('/files', express.static(path.join(__dirname, './public/files/'))); this.server.use('/files', express.static(path.join(__dirname, './public/files/')));
this.server.use('/history', express.static(path.join(__dirname, './public/history/'))); this.server.use('/css', express.static(path.resolve('./node_modules/bulma/css/')));
this.server.use('/css', express.static(path.join(__dirname, './public/css/')));
this.server.use('/js', express.static(path.join(__dirname, './public/js/'))); this.server.use('/js', express.static(path.join(__dirname, './public/js/')));
this.server.use('/js', express.static(path.resolve('./node_modules/jquery/dist/'))); this.server.use('/js', express.static(path.resolve('./node_modules/jquery/dist/')));
this.server.use('/js', express.static(path.resolve('./node_modules/socket.io-client/dist/'))); this.server.use('/js', express.static(path.resolve('./node_modules/socket.io-client/dist/')));
this.server.use('/css', express.static(path.resolve('./node_modules/bulma/css/')));
this.server.use('/css', express.static(path.join(__dirname, './public/css/')));
this.server.get('/', (req, res) => { this.server.get('/', (req, res) => {
const html = this.generateHomePage(); const html = this.generateHomePage();
@ -114,6 +114,15 @@ function Server () {
} }
}); });
this.server.get('/history', (req, res) => {
const html = this.generateHistoryPage();
if (html) {
res.send(html);
} else {
res.send('Something went wrong!');
}
});
this.io.on('connection', socket => { this.io.on('connection', socket => {
this.broadcastVisitors(); this.broadcastVisitors();
@ -171,27 +180,25 @@ Server.prototype.generateHomePage = function () {
if (bookData.hasOwnProperty('fileName')) return ''; if (bookData.hasOwnProperty('fileName')) return '';
const id = fileName.replace('.json', ''); const id = fileName.replace('.json', '');
const header = '<h2 class="title">' + bookData.title + '</h2><h4 class="subtitle">' + bookData.author + '</h4>'; const confirmId = 'confirm_' + id;
const content = '<div class="content"><h4>Contributed by ' + bookData.contributor + '</h4>' + snarkdown(bookData.summary) + '</div>' const added = fecha.format(new Date(bookData.added), 'dddd MMMM Do, YYYY');
+ this.fillTemplate('./templates/elements/modal.html', {
id: 'confirm_' + id,
content: this.fillTemplate('./templates/elements/messageBox.html', {
header: 'Download Your Book',
message: '<div class="content">'
+ '<p class="has-text-weight-bold">Please ensure that you\'re using a device that can download and save the file correctly!</p>'
+ '</div>'
+ '<div class="notification is-danger">After you leave or refresh this page, it will no longer be accessible to anyone!</div>'
+ '<div class="buttons">'
+ '<a class="button close">Cancel</a> <a class="button is-info take-book" data-book="' + id + '">I understand, give me the link!</a>'
+ '</div>',
}),
});
const footer = '<a class="button close">Close</a> <a class="button is-success modal-button" data-modal="confirm_' + id + '">Take Book</a>';
const modal = this.fillTemplate('./templates/elements/modalCard.html', { const modal = this.fillTemplate('./templates/elements/modalCard.html', {
id, id,
header, header: '<h2 class="title">' + bookData.title + '</h2><h4 class="subtitle">' + bookData.author + '</h4>',
content, content: this.fillTemplate('./templates/elements/bookInfo.html', {
footer, contributor: bookData.contributor,
fileFormat: bookData.fileFormat,
added,
summary: snarkdown(bookData.summary),
})
+ this.fillTemplate('./templates/elements/modal.html', {
id: confirmId,
content: this.fillTemplate('./templates/elements/messageBox.html', {
header: 'Download Your Book',
message: this.fillTemplate('./templates/elements/takeConfirm.html', { id }),
}),
}),
footer: '<a class="button close">Close</a> <a class="button is-success modal-button" data-modal="' + confirmId + '">Take Book</a>',
}); });
return this.fillTemplate('./templates/elements/book.html', { return this.fillTemplate('./templates/elements/book.html', {
id, id,
@ -201,10 +208,42 @@ Server.prototype.generateHomePage = function () {
modal, modal,
}); });
}).join(''); }).join('');
const body = this.fillTemplate('./templates/pages/booksList.html', { books }); const body = '<div class="columns is-multiline">' + books + '</div>';
return this.fillTemplate('./templates/htmlContainer.html', { title: 'View', body }); return this.fillTemplate('./templates/htmlContainer.html', { title: 'View', body });
} }
Server.prototype.generateHistoryPage = function () {
const files = fs.readdirSync(this.historyLocation).filter(fileName => fileName.includes('.json'));
const history = files.map(fileName => {
const bookData = JSON.parse(fs.readFileSync(path.resolve(this.historyLocation, fileName), 'utf8'));
const id = fileName.replace('.json', '');
const added = fecha.format(new Date(bookData.added), 'dddd MMMM Do, YYYY');
const removed = fecha.format(new Date(parseInt(id)), 'dddd MMMM Do, YYYY');
const removedTag = '<div class="control"><div class="tags has-addons"><span class="tag">Taken</span><span class="tag is-primary">' + removed + '</span></div></div>';
const modal = this.fillTemplate('./templates/elements/modalCard.html', {
id,
header: '<h2 class="title">' + bookData.title + '</h2><h4 class="subtitle">' + bookData.author + '</h4>',
content: this.fillTemplate('./templates/elements/bookInfo.html', {
contributor: bookData.contributor,
fileFormat: bookData.fileType,
added,
removedTag,
summary: snarkdown(bookData.summary),
}),
footer: '<a class="button close">Close</a>',
});
return this.fillTemplate('./templates/elements/book.html', {
id,
title: bookData.title,
author: bookData.author,
fileType: bookData.fileType,
modal,
});
}).join('');
const body = '<div class="columns is-multiline">' + history + '</div>';
return this.fillTemplate('./templates/htmlContainer.html', { title: 'History', resourcePath: '../', body });
}
Server.prototype.broadcastVisitors = function () { Server.prototype.broadcastVisitors = function () {
const numberConnected = this.io.of('/').clients().connected.length; const numberConnected = this.io.of('/').clients().connected.length;
this.io.emit('connected', numberConnected); this.io.emit('connected', numberConnected);
@ -226,6 +265,7 @@ Server.prototype.addBook = function (uploadData = {}, success = () => {}, error
author: uploadData.author.trim(), author: uploadData.author.trim(),
summary: uploadData.summary.trim(), summary: uploadData.summary.trim(),
contributor: uploadData.contributor.trim(), contributor: uploadData.contributor.trim(),
added: Date.now(),
fileType: book.name.substr(book.name.lastIndexOf('.')), fileType: book.name.substr(book.name.lastIndexOf('.')),
} }
@ -276,7 +316,7 @@ Server.prototype.deleteBooks = function (socketId) {
if (data.socketId === socketId) { if (data.socketId === socketId) {
const check = this.checkId(data.bookId, (bookPath, bookDataPath) => { const check = this.checkId(data.bookId, (bookPath, bookDataPath) => {
fs.unlinkSync(bookPath); fs.unlinkSync(bookPath);
fs.renameSync(bookDataPath, path.resolve(this.historyLocation, data.bookId + '.json')); fs.renameSync(bookDataPath, unusedFilename.sync(path.resolve(this.historyLocation, Date.now() + '.json')));
}); });
if (check === false) { if (check === false) {
console.log('couldn\'t find data.bookId'); console.log('couldn\'t find data.bookId');

View File

@ -6,5 +6,6 @@
"historyLocation": "./public/history/", "historyLocation": "./public/history/",
"maxLibrarySize": 0, "maxLibrarySize": 0,
"maxFileSize": 0, "maxFileSize": 0,
"maxHistory": 0,
"allowedFormats": [".epub", ".mobi", ".pdf"] "allowedFormats": [".epub", ".mobi", ".pdf"]
} }

View File

@ -0,0 +1,24 @@
<div class="content">
<h4>Contributed by {{contributor}}</h4>
<div class="field is-grouped is-grouped-multiline">
<div class="control">
<div class="tags has-addons">
<span class="tag">File Format</span>
<span class="tag is-primary">{{fileFormat}}</span>
</div>
</div>
<div class="control">
<div class="tags has-addons">
<span class="tag">Added</span>
<span class="tag is-primary">{{added}}</span>
</div>
</div>
{{removedTag}}
</div>
<div class="content">
<p>{{contributor}} said:</p>
{{summary}}
</div>
</div>

View File

@ -0,0 +1,7 @@
<div class="content">
<p class="has-text-weight-bold">Please ensure that you're using a device that can download and save the file correctly!</p>
</div>
<div class="notification is-danger">After you leave or refresh this page, it will no longer be accessible to anyone!</div>
<div class="buttons">
<a class="button close">Cancel</a> <a class="button is-info take-book" data-book="{{id}}">I understand, give me the link!</a>
</div>

View File

@ -9,11 +9,11 @@
<title>{{title}}{{titleSeparator}}{{siteTitle}}</title> <title>{{title}}{{titleSeparator}}{{siteTitle}}</title>
<meta name="description" content="A digital give-a-book, take-a-book library for ebooks"> <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"> <link rel="stylesheet" href="{{resourcePath}}css/bulma.min.css?v=1.0">
<link rel="stylesheet" href="./css/styles.css?v=1.0"> <link rel="stylesheet" href="{{resourcePath}}css/styles.css?v=1.0">
<script src="./js/jquery.min.js"></script> <script src="{{resourcePath}}js/jquery.min.js"></script>
<script src="./js/socket.io.js"></script> <script src="{{resourcePath}}js/socket.io.js"></script>
</head> </head>
<body> <body>
@ -39,6 +39,10 @@
<a class="navbar-item" href="/give"> <a class="navbar-item" href="/give">
Give Give
</a> </a>
<a class="navbar-item" href="/history/">
History
</a>
</div> </div>
<div class="navbar-end"> <div class="navbar-end">
@ -70,7 +74,7 @@
</footer> </footer>
{{modal}} {{modal}}
<script src="./js/little-library.js"></script> <script src="{{resourcePath}}js/little-library.js"></script>
</body> </body>
</html> </html>

View File

@ -1,3 +0,0 @@
<div class="columns is-multiline">
{{books}}
</div>

View File

@ -330,6 +330,11 @@ feature-policy@0.2.0:
resolved "https://registry.yarnpkg.com/feature-policy/-/feature-policy-0.2.0.tgz#22096de49ab240176878ffe2bde2f6ff04d48c43" resolved "https://registry.yarnpkg.com/feature-policy/-/feature-policy-0.2.0.tgz#22096de49ab240176878ffe2bde2f6ff04d48c43"
integrity sha512-2hGrlv6efG4hscYVZeaYjpzpT6I2OZgYqE2yDUzeAcKj2D1SH0AsEzqJNXzdoglEddcIXQQYop3lD97XpG75Jw== integrity sha512-2hGrlv6efG4hscYVZeaYjpzpT6I2OZgYqE2yDUzeAcKj2D1SH0AsEzqJNXzdoglEddcIXQQYop3lD97XpG75Jw==
fecha@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/fecha/-/fecha-3.0.2.tgz#fb3adb02762ab6dd27f7d5419f2f6c21a4229cd7"
integrity sha512-oJK6YbKtmz1uvuDsUHOmo9X2HKmYAcRWtzW2yrCzOJRUfyGUEu/8cDymBdedgEnkdJiTpNyPogWqfTuYffU4yA==
filename-reserved-regex@^2.0.0: filename-reserved-regex@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229" resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229"