UI improvements and bug fixes #8

Merged
hughrun merged 1 commit from publishing-fixes into main 2023-03-12 15:05:13 +11:00
10 changed files with 139 additions and 71 deletions

View file

@ -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')
})
})

View file

@ -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;
}

View file

@ -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>

View file

@ -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>

View file

@ -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
View 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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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 = ?'