mirror of
https://github.com/Luzifer/past3.git
synced 2024-12-22 20:31:16 +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:
parent
2ee228f721
commit
88cb7f1829
2 changed files with 87 additions and 12 deletions
92
app.js
92
app.js
|
@ -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">×</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
|
||||
function deleteFile(filename) {
|
||||
let s3 = new AWS.S3()
|
||||
|
@ -9,9 +32,7 @@ function deleteFile(filename) {
|
|||
|
||||
// error displays the error in the frontend
|
||||
function error(err) {
|
||||
let ed = $('#errorDisplay')
|
||||
ed.find('.alert').text(err)
|
||||
ed.show()
|
||||
showAlert('danger', err, 0)
|
||||
}
|
||||
|
||||
// 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
|
||||
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
|
||||
|
@ -83,6 +104,11 @@ function getMimeType(filename) {
|
|||
return mime
|
||||
}
|
||||
|
||||
// info displays the info in the frontend
|
||||
function info(msg) {
|
||||
showAlert('info', msg, 10000)
|
||||
}
|
||||
|
||||
// init initializes the interface with its listeners
|
||||
function init() {
|
||||
// Show sign-in modal
|
||||
|
@ -104,6 +130,21 @@ function init() {
|
|||
viewportMargin: 25,
|
||||
})
|
||||
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
|
||||
$('#filename').bind('input', filenameInput)
|
||||
|
@ -176,6 +217,27 @@ function loadFileIntoEditor(err, data) {
|
|||
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))
|
||||
}
|
||||
|
||||
|
@ -189,8 +251,13 @@ function loadFileList(err, data) {
|
|||
for (let obj of data.Contents) {
|
||||
let key = obj.Key.replace(getFilePrefix(), '')
|
||||
|
||||
let li = $(`<a href='#${key}' class='list-group-item file-list-item'><span class='badge'></span> ${key}</a>`)
|
||||
li.find('.badge').text(formatDate(obj.LastModified))
|
||||
let fileIcon = 'file-text-o'
|
||||
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)
|
||||
|
||||
if (key === $('#filename').val()) {
|
||||
|
@ -230,7 +297,18 @@ function saveFile(filename, content) {
|
|||
Bucket: window.past3_config.bucket,
|
||||
ContentType: mime.mime,
|
||||
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
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
{% endif %}
|
||||
|
||||
<style>
|
||||
#errorDisplay { display: none; }
|
||||
.file-mgmt { width: 100%; }
|
||||
.modal-body { text-align: center; }
|
||||
#signInButton { margin: 20px 164px 0; }
|
||||
|
@ -137,10 +136,8 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" id="errorDisplay">
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-danger"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12" id="errorDisplay"></div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
|
Loading…
Reference in a new issue