1
0
Fork 0
mirror of https://github.com/Luzifer/past3.git synced 2024-12-23 04:41:17 +00:00

Store changed documents locally to prevent data loss on reload

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2018-09-24 15:23:15 +02:00
parent 2ee228f721
commit 88cb7f1829
Signed by: luzifer
GPG key ID: DC2729FDD34BE99E
2 changed files with 87 additions and 12 deletions

92
app.js
View file

@ -1,3 +1,26 @@
const dateFormat = 'yyyy-MM-dd HH:mm:ss'
// showAlert creates an alert message and displays it
function showAlert(type, msg, timeout = 0, actions = {}) {
let alrt = $(`<div class="alert alert-${type} alert-dismissible"><button type="button" class="close" data-dismiss="alert">&times;</button> ${msg} </div>`)
for (let key in actions) {
let lnk = $(`<a href="#" class="alert-link">${key}</a>`)
lnk.bind('click', (e) => {
actions[key]()
$(e.target).parents('.alert').alert('close')
return false
})
lnk.appendTo(alrt)
}
alrt.appendTo($('#errorDisplay'))
if (timeout > 0) {
window.setTimeout(() => alrt.alert('close'), timeout)
}
}
// deleteFile removes the specified file from the AWS bucket // deleteFile removes the specified file from the AWS bucket
function deleteFile(filename) { function deleteFile(filename) {
let s3 = new AWS.S3() let s3 = new AWS.S3()
@ -9,9 +32,7 @@ function deleteFile(filename) {
// error displays the error in the frontend // error displays the error in the frontend
function error(err) { function error(err) {
let ed = $('#errorDisplay') showAlert('danger', err, 0)
ed.find('.alert').text(err)
ed.show()
} }
// fileActionCallback is used to trigger a reload of the file list // fileActionCallback is used to trigger a reload of the file list
@ -31,7 +52,7 @@ function filenameInput() {
// formatDate formats a Date() object into iso-like format // formatDate formats a Date() object into iso-like format
function formatDate(src) { function formatDate(src) {
return $.format.date(src, 'yyyy-MM-dd HH:mm:ss') return $.format.date(src, dateFormat)
} }
// getAWSCredentials retrieves AWS credentials via Cognito using the Google ID Token // getAWSCredentials retrieves AWS credentials via Cognito using the Google ID Token
@ -83,6 +104,11 @@ function getMimeType(filename) {
return mime return mime
} }
// info displays the info in the frontend
function info(msg) {
showAlert('info', msg, 10000)
}
// init initializes the interface with its listeners // init initializes the interface with its listeners
function init() { function init() {
// Show sign-in modal // Show sign-in modal
@ -104,6 +130,21 @@ function init() {
viewportMargin: 25, viewportMargin: 25,
}) })
window.editor.setSize(null, '100%') window.editor.setSize(null, '100%')
window.editor.on('change', (cm, change) => {
if (change.origin === 'setValue') {
// Do not cache file when a set was done
return
}
// Store a local copy
let filename = $('#filename').val()
if (filename !== '') {
window.localStorage.setItem(filename, JSON.stringify({
date: new Date(),
text: btoa(cm.getValue()),
}))
}
})
// Set up bindings // Set up bindings
$('#filename').bind('input', filenameInput) $('#filename').bind('input', filenameInput)
@ -176,6 +217,27 @@ function loadFileIntoEditor(err, data) {
return error(err) return error(err)
} }
let lastModified = new Date(data.LastModified)
let cached = window.localStorage.getItem($('#filename').val())
if (cached !== null) {
let cd = JSON.parse(cached)
let cdChange = new Date(cd.date)
if (lastModified < cdChange) {
window.editor.setValue(atob(cd.text))
showAlert('info', `Recovered working copy stored ${formatDate(cdChange)}`, 10000, {
'Dismiss working copy': () => {
window.localStorage.removeItem($('#filename').val())
getFile($('#filename').val())
listFiles()
return false
},
})
return
}
}
window.editor.setValue(String(data.Body)) window.editor.setValue(String(data.Body))
} }
@ -189,8 +251,13 @@ function loadFileList(err, data) {
for (let obj of data.Contents) { for (let obj of data.Contents) {
let key = obj.Key.replace(getFilePrefix(), '') let key = obj.Key.replace(getFilePrefix(), '')
let li = $(`<a href='#${key}' class='list-group-item file-list-item'><span class='badge'></span> ${key}</a>`) let fileIcon = 'file-text-o'
li.find('.badge').text(formatDate(obj.LastModified)) if (window.localStorage.getItem(key) !== null) {
fileIcon = 'file-text'
}
let li = $(`<a href='#${key}' class='list-group-item file-list-item'><span class='badge'></span> <i class="fa fa-${fileIcon}"></i> ${key}</a>`)
li.find('.badge').text(`${formatDate(obj.LastModified)}`)
li.data('file', key) li.data('file', key)
if (key === $('#filename').val()) { if (key === $('#filename').val()) {
@ -230,7 +297,18 @@ function saveFile(filename, content) {
Bucket: window.past3_config.bucket, Bucket: window.past3_config.bucket,
ContentType: mime.mime, ContentType: mime.mime,
Key: getFilePrefix() + filename, Key: getFilePrefix() + filename,
}, fileActionCallback) }, saveFileCallback)
}
// saveFileCallback is triggered after a file save and removes the local copy of the file
function saveFileCallback(err, data) {
if (err) {
return error(err)
}
window.localStorage.removeItem($('#filename').val())
fileActionCallback(err, data)
} }
// setEditorMime updates the mime type of the file content loaded into the editor // setEditorMime updates the mime type of the file content loaded into the editor

View file

@ -22,7 +22,6 @@
{% endif %} {% endif %}
<style> <style>
#errorDisplay { display: none; }
.file-mgmt { width: 100%; } .file-mgmt { width: 100%; }
.modal-body { text-align: center; } .modal-body { text-align: center; }
#signInButton { margin: 20px 164px 0; } #signInButton { margin: 20px 164px 0; }
@ -137,10 +136,8 @@
</div> </div>
</div> </div>
<div class="row" id="errorDisplay"> <div class="row">
<div class="col-md-12"> <div class="col-md-12" id="errorDisplay"></div>
<div class="alert alert-danger"></div>
</div>
</div> </div>
<div class="row"> <div class="row">