UI improvements and bug fixes #8
43
server.js
43
server.js
|
@ -48,18 +48,7 @@ app.set('view engine', 'spy');
|
|||
// GET
|
||||
|
||||
app.get('/', requireLoggedIn, (req, res) => {
|
||||
let data = {
|
||||
title: 'Home',
|
||||
disabled: '',
|
||||
message: getSavedFile(req.session.user.username)
|
||||
}
|
||||
let today = getNow().toISOString().slice(0,10)
|
||||
let latestPost = req.session.user.latest_post
|
||||
if (today === latestPost) {
|
||||
data.disabled = 'disabled'
|
||||
data.message = `Relax, ${req.session.user.username}, you have already posted today.`
|
||||
}
|
||||
res.render('index.spy', data)
|
||||
res.render('index.spy', {title: 'Soyuz home', writeNew: getSavedFile(req.session.user.username)? 'Return to draft' : 'New'})
|
||||
})
|
||||
|
||||
app.get('/login', (req, res) => {
|
||||
|
@ -70,9 +59,33 @@ app.get('/login', (req, res) => {
|
|||
}
|
||||
})
|
||||
|
||||
app.get('/new', requireLoggedIn, (req, res) => {
|
||||
let message = getSavedFile(req.session.user.username) || "# Title of my note"
|
||||
let data = {
|
||||
title: 'New post',
|
||||
disabled: '',
|
||||
message: message
|
||||
}
|
||||
let today = getNow().toISOString().slice(0,10)
|
||||
// check whether user has already posted today
|
||||
return getLatestPost(req.session.user.directory, true, (dateString)=> {
|
||||
if (today === dateString) {
|
||||
data.disabled = 'disabled'
|
||||
data.message = `Relax, ${req.session.user.username}, you have already posted today.`
|
||||
}
|
||||
res.render('new.spy', data)
|
||||
})
|
||||
})
|
||||
|
||||
app.get('/edit', requireLoggedIn, (req, res) => {
|
||||
getLatestPost( req.session.user.directory, (data, path) => {
|
||||
res.render('edit.spy', {data: data, path: path, title: 'Edit'})
|
||||
return getLatestPost(req.session.user.directory, true, (dateString) => {
|
||||
if (dateString) {
|
||||
return getLatestPost( req.session.user.directory, false, (message, path) => {
|
||||
res.render('edit.spy', {message: message, path: path, title: 'Edit'})
|
||||
})
|
||||
} else {
|
||||
res.redirect('/new')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -118,7 +131,7 @@ app.post('/publish', requireLoggedIn, (req, res) => {
|
|||
|
||||
app.post('/save', requireLoggedIn, (req, res) => {
|
||||
saveFile(req.session.user.username, req.body.textarea, () => {
|
||||
res.redirect('/')
|
||||
res.redirect('/new')
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -77,11 +77,34 @@ main {
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
.home-menu {
|
||||
margin: auto;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.home-button {
|
||||
background: none;
|
||||
color: inherit;
|
||||
border: none;
|
||||
padding: 0;
|
||||
font: 2em sans-serif;
|
||||
cursor: pointer;
|
||||
outline: inherit;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
background-color: #999;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
section.disabled {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.password-reset {
|
||||
width: 100%;
|
||||
margin-bottom: 1em;
|
||||
|
@ -100,3 +123,9 @@ main {
|
|||
.menu-help {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.alert {
|
||||
margin: auto;
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
<< partials/head >>
|
||||
<body>
|
||||
<< partials/header >>
|
||||
<header>
|
||||
<a class="action-button" href="/">Home</a> |
|
||||
<a class="action-button" href="/settings">Settings</a> |
|
||||
<a class="action-button" href="/help">Help</a>
|
||||
</header>
|
||||
|
||||
<section class="textarea">
|
||||
<form method="post">
|
||||
<textarea name="textarea" autofocus>{{ data }}</textarea>
|
||||
<textarea name="textarea" autofocus class="{{ disabled }}" {{ disabled }}>{{ message }}</textarea>
|
||||
<input type="text" name="path" value="{{ path }}" hidden>
|
||||
<section class="post-buttons">
|
||||
<input class="action-button" type="submit" name="save" value="Save" formaction="/save"> |
|
||||
<input class="action-button" type="submit" name="publish" value="Update" formaction="/update">
|
||||
</section>
|
||||
</form>
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<< partials/head >>
|
||||
<body>
|
||||
<< partials/header >>
|
||||
<header>
|
||||
<a class="action-button" href="/">Home</a> |
|
||||
<a class="action-button" href="/settings">Settings</a>
|
||||
</header>
|
||||
<main id="settings">
|
||||
<h1>Help</h1>
|
||||
<p>Soyuz is a web app for writing Gemini posts.</p>
|
||||
|
|
|
@ -1,17 +1,10 @@
|
|||
<< partials/head >>
|
||||
<body class="{{ disabled }}">
|
||||
<< partials/header >>
|
||||
|
||||
<section class="textarea">
|
||||
<form method="post">
|
||||
<textarea name="textarea" autofocus class="{{ disabled }}" {{ disabled }}>{{ message }}</textarea>
|
||||
<section class="post-buttons">
|
||||
<input class="action-button" type="submit" name="save" value="Save" formaction="/save"> |
|
||||
<input class="action-button" type="submit" name="publish" value="Publish" formaction="/publish">
|
||||
</section>
|
||||
</form>
|
||||
</section>
|
||||
<footer>
|
||||
</footer>
|
||||
<menu class="home-menu">
|
||||
<a class="home-button" href="/new">{{ writeNew }}</a><br/>
|
||||
<a class="home-button" href="/edit">Edit previous</a><br/>
|
||||
<a class="home-button" href="/settings">Settings</a><br/>
|
||||
<a class="home-button" href="/help">Help</a>
|
||||
</menu>
|
||||
</body>
|
||||
</html>
|
||||
|
|
21
templates/new.spy
Normal file
21
templates/new.spy
Normal file
|
@ -0,0 +1,21 @@
|
|||
<< partials/head >>
|
||||
<body class="{{ disabled }}">
|
||||
<header>
|
||||
<a class="action-button" href="/">Home</a> |
|
||||
<a class="action-button" href="/settings">Settings</a> |
|
||||
<a class="action-button" href="/help">Help</a>
|
||||
</header>
|
||||
<section class="textarea">
|
||||
<section class="alert">{{ alert }}</section>
|
||||
<form method="post">
|
||||
<textarea name="textarea" autofocus class="{{ disabled }}" {{ disabled }}>{{ message }}</textarea>
|
||||
<section class="post-buttons {{ disabled }}">
|
||||
<input class="action-button" type="submit" name="save" value="Save draft" formaction="/save"> |
|
||||
<input class="action-button" type="submit" name="publish" value="Publish" formaction="/publish">
|
||||
</section>
|
||||
</form>
|
||||
</section>
|
||||
<footer>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
|
@ -1,6 +0,0 @@
|
|||
<header>
|
||||
<a class="action-button" href="/">New</a> |
|
||||
<a class="action-button" href="/edit">Edit</a> |
|
||||
<a class="action-button" href="/settings">Settings</a> |
|
||||
<a class="action-button" href="/help">Help</a>
|
||||
</header>
|
|
@ -1,7 +1,12 @@
|
|||
<< partials/head >>
|
||||
<body>
|
||||
<section>
|
||||
<< partials/header >>
|
||||
<header>
|
||||
<a class="action-button" href="/">Home</a> |
|
||||
<a class="action-button" href="/edit">Edit</a> |
|
||||
<a class="action-button" href="/settings">Settings</a> |
|
||||
<a class="action-button" href="/help">Help</a>
|
||||
</header>
|
||||
<main>
|
||||
<h2> {{ title }}</h2>
|
||||
<p>Hooray 🎉</p>
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<< partials/head >>
|
||||
<body>
|
||||
<< partials/header >>
|
||||
<header>
|
||||
<a class="action-button" href="/">Home</a> |
|
||||
<a class="action-button" href="/help">Help</a>
|
||||
</header>
|
||||
<main id="settings">
|
||||
<form method="post">
|
||||
<section>
|
||||
|
|
66
utilities.js
66
utilities.js
|
@ -35,7 +35,7 @@ const addUser = function(username, directory, callback){
|
|||
let stmt = db.prepare(
|
||||
'INSERT INTO users (username, directory, password, salt, saved_post) VALUES (?, ?, ?, ?, ?)'
|
||||
);
|
||||
stmt.run(username, directory, hash, salt, '# Title of my note');
|
||||
stmt.run(username, directory, hash, salt, null);
|
||||
return callback(password)
|
||||
});
|
||||
}
|
||||
|
@ -63,17 +63,6 @@ const resetPassword = function(username, pass, callback) {
|
|||
});
|
||||
}
|
||||
|
||||
// update latest post in db
|
||||
const updateLatestPostDate = function(username, callback) {
|
||||
|
||||
let dateString = getNow().toISOString().slice(0,10)
|
||||
let stmt = db.prepare(
|
||||
'UPDATE users SET latest_post = ? WHERE username = ?'
|
||||
);
|
||||
stmt.run(dateString, username);
|
||||
callback(dateString)
|
||||
}
|
||||
|
||||
// AUTHORISATION MIDDLEWARE
|
||||
const verifyUser = function (req, res, next) {
|
||||
let username = req.body.username
|
||||
|
@ -96,9 +85,8 @@ const verifyUser = function (req, res, next) {
|
|||
}
|
||||
req.session.user = {
|
||||
username: user.username,
|
||||
directory: user.directory,
|
||||
latest_post: user.latest_post,
|
||||
};
|
||||
directory: user.directory
|
||||
}
|
||||
next()
|
||||
});
|
||||
}
|
||||
|
@ -147,11 +135,8 @@ const publishNewPost = function(req, cb) {
|
|||
})
|
||||
})
|
||||
// clear any saved post now that it is published
|
||||
saveFile(req.session.user.username, '# Title of my note', () => {
|
||||
return updateLatestPostDate(req.session.user.username, datestring => {
|
||||
req.session.user.latest_post = datestring
|
||||
return cb()
|
||||
})
|
||||
saveFile(req.session.user.username, null, () => {
|
||||
return cb()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -176,7 +161,6 @@ const publishNewPost = function(req, cb) {
|
|||
newlines.push(line)
|
||||
}
|
||||
}
|
||||
lines[0] = '## Latest notes'
|
||||
newlines.unshift(`## Latest notes\n\n=> /${year}/${dateString}.gmi ${dateString} (${title})`)
|
||||
updated = newlines.join('\n')
|
||||
writeFile(indexFile, updated, (err) => {
|
||||
|
@ -213,22 +197,38 @@ const publishNewPost = function(req, cb) {
|
|||
})
|
||||
}
|
||||
|
||||
let getLatestPost = function(directory, callback) {
|
||||
let getLatestPost = function(directory, dateOnly, callback) {
|
||||
// we check the index file because
|
||||
// a new post could have come from
|
||||
// somewhere other than the app
|
||||
// e.g. from a CLI on a laptop etc
|
||||
let indexFile = `${GEMINI_PATH}/${directory}/index.gmi`
|
||||
readFile(indexFile, {encoding: 'utf8'}, (err, data) => {
|
||||
if (err) throw err;
|
||||
if (err) {
|
||||
if (err.code == 'ENOENT') {
|
||||
return callback(null)
|
||||
}
|
||||
else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
if (data.length == 0 || data == null) {
|
||||
return callback(null)
|
||||
}
|
||||
|
||||
let links = data.split('## Latest notes')
|
||||
let parts = links[1].split('\n')[2].split(' ')
|
||||
let filePath = `${GEMINI_PATH}/${directory}/${parts[1]}`
|
||||
|
||||
readFile(filePath, {encoding: 'utf8'}, (err, file) => {
|
||||
if (err) throw err;
|
||||
return callback(file, filePath)
|
||||
})
|
||||
if (dateOnly) {
|
||||
let dateString = filePath.slice(-14,-4)
|
||||
return callback(dateString)
|
||||
} else {
|
||||
readFile(filePath, {encoding: 'utf8'}, (err, file) => {
|
||||
if (err) throw err;
|
||||
return callback(file, filePath)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -237,9 +237,12 @@ let updatePost = function(req, callback) {
|
|||
let path = req.body.path
|
||||
let title = contents.split('\n')[0].split('# ')[1].trim()
|
||||
let year = getNow().toISOString().slice(0,4)
|
||||
let dateString = getNow().toISOString().slice(0,10)
|
||||
let indexFile = `${GEMINI_PATH}/${req.session.user.directory}/index.gmi`
|
||||
let yearIndex = `${GEMINI_PATH}/${req.session.user.directory}/${year}/index.gmi`
|
||||
let relative_path = path.slice(-20)
|
||||
let post_date = relative_path.slice(6,16)
|
||||
let prefix = `=> ${relative_path} ${post_date}`
|
||||
let archivePrefix = `=> ${relative_path.slice(6)} ${post_date}`
|
||||
let updated = ''
|
||||
|
||||
// we update the index and archive listings in case the title has changed
|
||||
|
@ -250,7 +253,7 @@ let updatePost = function(req, callback) {
|
|||
let links = data.split('## Latest notes')
|
||||
let lines = links[1].split('\n')
|
||||
lines[0] = '## Latest notes'
|
||||
lines[2] = `=> /${year}/${dateString}.gmi ${dateString} (${title})`
|
||||
lines[2] = `${prefix} (${title})`
|
||||
updated = links[0] + lines.join('\n')
|
||||
// update index on homepage
|
||||
writeFile(indexFile, updated, (err) => {
|
||||
|
@ -260,7 +263,7 @@ let updatePost = function(req, callback) {
|
|||
throw err
|
||||
} else {
|
||||
let lines = data.split('\n')
|
||||
lines[2] = `=> ${dateString}.gmi ${dateString} (${title})`
|
||||
lines[2] = `${archivePrefix} (${title})`
|
||||
updated = lines.join('\n')
|
||||
// update archive page
|
||||
writeFile(yearIndex, updated, (err) => {
|
||||
|
@ -280,6 +283,7 @@ let updatePost = function(req, callback) {
|
|||
})
|
||||
}
|
||||
|
||||
// NOTE: this refers to saving the file on the database, not publishing the file to the server
|
||||
let saveFile = function(user, text, callback) {
|
||||
let stmt = db.prepare(
|
||||
'UPDATE users SET saved_post = ? WHERE username = ?'
|
||||
|
|
Loading…
Reference in a new issue