parent
5c94d23466
commit
9624c4db00
|
@ -361,6 +361,24 @@ body#pad {
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body#pad .alert {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0.25em;
|
||||||
|
left: 2em;
|
||||||
|
right: 2em;
|
||||||
|
font-size: 1.1em;
|
||||||
|
|
||||||
|
&#edited-elsewhere {
|
||||||
|
&.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media all and (max-height: 500px) {
|
@media all and (max-height: 500px) {
|
||||||
body#pad {
|
body#pad {
|
||||||
textarea {
|
textarea {
|
||||||
|
@ -425,6 +443,10 @@ body#pad {
|
||||||
padding-left: 10%;
|
padding-left: 10%;
|
||||||
padding-right: 10%;
|
padding-right: 10%;
|
||||||
}
|
}
|
||||||
|
.alert {
|
||||||
|
left: 10%;
|
||||||
|
right: 10%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media all and (min-width: 60em) {
|
@media all and (min-width: 60em) {
|
||||||
|
@ -433,6 +455,10 @@ body#pad {
|
||||||
padding-left: 15%;
|
padding-left: 15%;
|
||||||
padding-right: 15%;
|
padding-right: 15%;
|
||||||
}
|
}
|
||||||
|
.alert {
|
||||||
|
left: 15%;
|
||||||
|
right: 15%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media all and (min-width: 70em) {
|
@media all and (min-width: 70em) {
|
||||||
|
@ -441,6 +467,10 @@ body#pad {
|
||||||
padding-left: 20%;
|
padding-left: 20%;
|
||||||
padding-right: 20%;
|
padding-right: 20%;
|
||||||
}
|
}
|
||||||
|
.alert {
|
||||||
|
left: 20%;
|
||||||
|
right: 20%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media all and (min-width: 85em) {
|
@media all and (min-width: 85em) {
|
||||||
|
@ -449,6 +479,10 @@ body#pad {
|
||||||
padding-left: 25%;
|
padding-left: 25%;
|
||||||
padding-right: 25%;
|
padding-right: 25%;
|
||||||
}
|
}
|
||||||
|
.alert {
|
||||||
|
left: 25%;
|
||||||
|
right: 25%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media all and (min-width: 105em) {
|
@media all and (min-width: 105em) {
|
||||||
|
@ -457,6 +491,10 @@ body#pad {
|
||||||
padding-left: 30%;
|
padding-left: 30%;
|
||||||
padding-right: 30%;
|
padding-right: 30%;
|
||||||
}
|
}
|
||||||
|
.alert {
|
||||||
|
left: 30%;
|
||||||
|
right: 30%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media (pointer: coarse) {
|
@media (pointer: coarse) {
|
||||||
|
|
21
posts.go
21
posts.go
|
@ -135,6 +135,7 @@ type (
|
||||||
Views int64
|
Views int64
|
||||||
Font string
|
Font string
|
||||||
Created time.Time
|
Created time.Time
|
||||||
|
Updated time.Time
|
||||||
IsRTL sql.NullBool
|
IsRTL sql.NullBool
|
||||||
Language sql.NullString
|
Language sql.NullString
|
||||||
OwnerID int64
|
OwnerID int64
|
||||||
|
@ -1240,9 +1241,9 @@ func getRawPost(app *App, friendlyID string) *RawPost {
|
||||||
var isRTL sql.NullBool
|
var isRTL sql.NullBool
|
||||||
var lang sql.NullString
|
var lang sql.NullString
|
||||||
var ownerID sql.NullInt64
|
var ownerID sql.NullInt64
|
||||||
var created time.Time
|
var created, updated time.Time
|
||||||
|
|
||||||
err := app.db.QueryRow("SELECT title, content, text_appearance, language, rtl, created, owner_id FROM posts WHERE id = ?", friendlyID).Scan(&title, &content, &font, &lang, &isRTL, &created, &ownerID)
|
err := app.db.QueryRow("SELECT title, content, text_appearance, language, rtl, created, updated, owner_id FROM posts WHERE id = ?", friendlyID).Scan(&title, &content, &font, &lang, &isRTL, &created, &updated, &ownerID)
|
||||||
switch {
|
switch {
|
||||||
case err == sql.ErrNoRows:
|
case err == sql.ErrNoRows:
|
||||||
return &RawPost{Content: "", Found: false, Gone: false}
|
return &RawPost{Content: "", Found: false, Gone: false}
|
||||||
|
@ -1250,7 +1251,7 @@ func getRawPost(app *App, friendlyID string) *RawPost {
|
||||||
return &RawPost{Content: "", Found: true, Gone: false}
|
return &RawPost{Content: "", Found: true, Gone: false}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &RawPost{Title: title, Content: content, Font: font, Created: created, IsRTL: isRTL, Language: lang, OwnerID: ownerID.Int64, Found: true, Gone: content == ""}
|
return &RawPost{Title: title, Content: content, Font: font, Created: created, Updated: updated, IsRTL: isRTL, Language: lang, OwnerID: ownerID.Int64, Found: true, Gone: content == ""}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1259,15 +1260,15 @@ func getRawCollectionPost(app *App, slug, collAlias string) *RawPost {
|
||||||
var id, title, content, font string
|
var id, title, content, font string
|
||||||
var isRTL sql.NullBool
|
var isRTL sql.NullBool
|
||||||
var lang sql.NullString
|
var lang sql.NullString
|
||||||
var created time.Time
|
var created, updated time.Time
|
||||||
var ownerID null.Int
|
var ownerID null.Int
|
||||||
var views int64
|
var views int64
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if app.cfg.App.SingleUser {
|
if app.cfg.App.SingleUser {
|
||||||
err = app.db.QueryRow("SELECT id, title, content, text_appearance, language, rtl, view_count, created, owner_id FROM posts WHERE slug = ? AND collection_id = 1", slug).Scan(&id, &title, &content, &font, &lang, &isRTL, &views, &created, &ownerID)
|
err = app.db.QueryRow("SELECT id, title, content, text_appearance, language, rtl, view_count, created, updated, owner_id FROM posts WHERE slug = ? AND collection_id = 1", slug).Scan(&id, &title, &content, &font, &lang, &isRTL, &views, &created, &updated, &ownerID)
|
||||||
} else {
|
} else {
|
||||||
err = app.db.QueryRow("SELECT id, title, content, text_appearance, language, rtl, view_count, created, owner_id FROM posts WHERE slug = ? AND collection_id = (SELECT id FROM collections WHERE alias = ?)", slug, collAlias).Scan(&id, &title, &content, &font, &lang, &isRTL, &views, &created, &ownerID)
|
err = app.db.QueryRow("SELECT id, title, content, text_appearance, language, rtl, view_count, created, updated, owner_id FROM posts WHERE slug = ? AND collection_id = (SELECT id FROM collections WHERE alias = ?)", slug, collAlias).Scan(&id, &title, &content, &font, &lang, &isRTL, &views, &created, &updated, &ownerID)
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case err == sql.ErrNoRows:
|
case err == sql.ErrNoRows:
|
||||||
|
@ -1283,6 +1284,7 @@ func getRawCollectionPost(app *App, slug, collAlias string) *RawPost {
|
||||||
Content: content,
|
Content: content,
|
||||||
Font: font,
|
Font: font,
|
||||||
Created: created,
|
Created: created,
|
||||||
|
Updated: updated,
|
||||||
IsRTL: isRTL,
|
IsRTL: isRTL,
|
||||||
Language: lang,
|
Language: lang,
|
||||||
OwnerID: ownerID.Int64,
|
OwnerID: ownerID.Int64,
|
||||||
|
@ -1543,6 +1545,13 @@ func (rp *RawPost) Created8601() string {
|
||||||
return rp.Created.Format("2006-01-02T15:04:05Z")
|
return rp.Created.Format("2006-01-02T15:04:05Z")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rp *RawPost) Updated8601() string {
|
||||||
|
if rp.Updated.IsZero() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return rp.Updated.Format("2006-01-02T15:04:05Z")
|
||||||
|
}
|
||||||
|
|
||||||
var imageURLRegex = regexp.MustCompile(`(?i)[^ ]+\.(gif|png|jpg|jpeg|image)$`)
|
var imageURLRegex = regexp.MustCompile(`(?i)[^ ]+\.(gif|png|jpg|jpeg|image)$`)
|
||||||
|
|
||||||
func (p *Post) extractImages() {
|
func (p *Post) extractImages() {
|
||||||
|
|
|
@ -116,13 +116,27 @@ var H = {
|
||||||
save: function($el, key) {
|
save: function($el, key) {
|
||||||
localStorage.setItem(key, $el.el.value);
|
localStorage.setItem(key, $el.el.value);
|
||||||
},
|
},
|
||||||
load: function($el, key, onlyLoadPopulated) {
|
load: function($el, key, onlyLoadPopulated, postUpdated) {
|
||||||
var val = localStorage.getItem(key);
|
var val = localStorage.getItem(key);
|
||||||
if (onlyLoadPopulated && val == null) {
|
if (onlyLoadPopulated && val == null) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
$el.el.value = val;
|
$el.el.value = val;
|
||||||
|
if (postUpdated != null) {
|
||||||
|
var lastLocalPublishStr = localStorage.getItem(key+'-published');
|
||||||
|
if (lastLocalPublishStr != null && lastLocalPublishStr != '') {
|
||||||
|
try {
|
||||||
|
var lastLocalPublish = new Date(lastLocalPublishStr);
|
||||||
|
if (postUpdated > lastLocalPublish) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("unable to parse draft updated time");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
set: function(key, value) {
|
set: function(key, value) {
|
||||||
localStorage.setItem(key, value);
|
localStorage.setItem(key, value);
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
{{end}}{{.Post.Content}}</textarea>
|
{{end}}{{.Post.Content}}</textarea>
|
||||||
|
|
||||||
|
<div class="alert success hidden" id="edited-elsewhere">This post has been updated elsewhere since you last published! <a href="#" id="erase-edit">Delete draft and reload</a>.</div>
|
||||||
|
|
||||||
<header id="tools">
|
<header id="tools">
|
||||||
<div id="clip">
|
<div id="clip">
|
||||||
{{if not .SingleUser}}<h1>{{if .Chorus}}<a href="/" title="Home">{{else}}<a href="/me/c/" title="View blogs">{{end}}{{.SiteName}}</a></h1>{{end}}
|
{{if not .SingleUser}}<h1>{{if .Chorus}}<a href="/" title="Home">{{else}}<a href="/me/c/" title="View blogs">{{end}}{{.SiteName}}</a></h1>{{end}}
|
||||||
|
@ -36,6 +38,7 @@
|
||||||
<script>
|
<script>
|
||||||
var $writer = H.getEl('writer');
|
var $writer = H.getEl('writer');
|
||||||
var $btnPublish = H.getEl('publish');
|
var $btnPublish = H.getEl('publish');
|
||||||
|
var $btnEraseEdit = H.getEl('edited-elsewhere');
|
||||||
var $wc = H.getEl("wc");
|
var $wc = H.getEl("wc");
|
||||||
var updateWordCount = function() {
|
var updateWordCount = function() {
|
||||||
var words = 0;
|
var words = 0;
|
||||||
|
@ -58,7 +61,17 @@
|
||||||
};
|
};
|
||||||
{{if .Post.Id}}var draftDoc = 'draft{{.Post.Id}}';
|
{{if .Post.Id}}var draftDoc = 'draft{{.Post.Id}}';
|
||||||
var origDoc = '{{.Post.Content}}';{{else}}var draftDoc = 'lastDoc';{{end}}
|
var origDoc = '{{.Post.Content}}';{{else}}var draftDoc = 'lastDoc';{{end}}
|
||||||
H.load($writer, draftDoc, true);
|
var updatedStr = '{{.Post.Updated8601}}';
|
||||||
|
var updated = null;
|
||||||
|
if (updatedStr != '') {
|
||||||
|
updated = new Date(updatedStr);
|
||||||
|
}
|
||||||
|
var ok = H.load($writer, draftDoc, true, updated);
|
||||||
|
if (!ok) {
|
||||||
|
// Show "edited elsewhere" warning
|
||||||
|
$btnEraseEdit.el.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
var defaultTimeSet = false;
|
||||||
updateWordCount();
|
updateWordCount();
|
||||||
|
|
||||||
var typingTimer;
|
var typingTimer;
|
||||||
|
@ -130,6 +143,7 @@
|
||||||
data = JSON.parse(http.responseText);
|
data = JSON.parse(http.responseText);
|
||||||
id = data.data.id;
|
id = data.data.id;
|
||||||
nextURL = '{{if .SingleUser}}/d{{end}}/'+id;
|
nextURL = '{{if .SingleUser}}/d{{end}}/'+id;
|
||||||
|
localStorage.setItem('draft'+id+'-published', new Date().toISOString());
|
||||||
|
|
||||||
{{ if not .Post.Id }}
|
{{ if not .Post.Id }}
|
||||||
// Post created
|
// Post created
|
||||||
|
@ -198,6 +212,13 @@
|
||||||
publish(content, selectedFont);
|
publish(content, selectedFont);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
H.getEl('erase-edit').on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
H.remove(draftDoc);
|
||||||
|
H.remove(draftDoc+'-published');
|
||||||
|
justPublished = true; // Block auto-save
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
|
|
||||||
WebFontConfig = {
|
WebFontConfig = {
|
||||||
custom: { families: [ 'Lora:400,700:latin' ], urls: [ '/css/fonts.css' ] }
|
custom: { families: [ 'Lora:400,700:latin' ], urls: [ '/css/fonts.css' ] }
|
||||||
|
@ -207,12 +228,20 @@
|
||||||
var doneTyping = function() {
|
var doneTyping = function() {
|
||||||
if (draftDoc == 'lastDoc' || $writer.el.value != origDoc) {
|
if (draftDoc == 'lastDoc' || $writer.el.value != origDoc) {
|
||||||
H.save($writer, draftDoc);
|
H.save($writer, draftDoc);
|
||||||
|
if (!defaultTimeSet) {
|
||||||
|
var lastLocalPublishStr = localStorage.getItem(draftDoc+'-published');
|
||||||
|
if (lastLocalPublishStr == null || lastLocalPublishStr == '') {
|
||||||
|
localStorage.setItem(draftDoc+'-published', updatedStr);
|
||||||
|
}
|
||||||
|
defaultTimeSet = true;
|
||||||
|
}
|
||||||
updateWordCount();
|
updateWordCount();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
window.addEventListener('beforeunload', function(e) {
|
window.addEventListener('beforeunload', function(e) {
|
||||||
if (draftDoc != 'lastDoc' && $writer.el.value == origDoc) {
|
if (draftDoc != 'lastDoc' && $writer.el.value == origDoc) {
|
||||||
H.remove(draftDoc);
|
H.remove(draftDoc);
|
||||||
|
H.remove(draftDoc+'-published');
|
||||||
} else if (!justPublished) {
|
} else if (!justPublished) {
|
||||||
doneTyping();
|
doneTyping();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
{{end}}{{.Post.Content}}</textarea>
|
{{end}}{{.Post.Content}}</textarea>
|
||||||
|
|
||||||
|
<div class="alert success hidden" id="edited-elsewhere">This post has been updated elsewhere since you last published! <a href="#" id="erase-edit">Delete draft and reload</a>.</div>
|
||||||
|
|
||||||
<header id="tools">
|
<header id="tools">
|
||||||
<div id="clip">
|
<div id="clip">
|
||||||
{{if not .SingleUser}}<h1><a href="/me/c/" title="View blogs"><img class="ic-24dp" src="/img/ic_blogs_dark@2x.png" /></a></h1>{{end}}
|
{{if not .SingleUser}}<h1><a href="/me/c/" title="View blogs"><img class="ic-24dp" src="/img/ic_blogs_dark@2x.png" /></a></h1>{{end}}
|
||||||
|
@ -88,6 +90,7 @@
|
||||||
}
|
}
|
||||||
var $writer = H.getEl('writer');
|
var $writer = H.getEl('writer');
|
||||||
var $btnPublish = H.getEl('publish');
|
var $btnPublish = H.getEl('publish');
|
||||||
|
var $btnEraseEdit = H.getEl('edited-elsewhere');
|
||||||
var $wc = H.getEl("wc");
|
var $wc = H.getEl("wc");
|
||||||
var updateWordCount = function() {
|
var updateWordCount = function() {
|
||||||
var words = 0;
|
var words = 0;
|
||||||
|
@ -110,7 +113,17 @@
|
||||||
};
|
};
|
||||||
{{if .Post.Id}}var draftDoc = 'draft{{.Post.Id}}';
|
{{if .Post.Id}}var draftDoc = 'draft{{.Post.Id}}';
|
||||||
var origDoc = '{{.Post.Content}}';{{else}}var draftDoc = 'lastDoc';{{end}}
|
var origDoc = '{{.Post.Content}}';{{else}}var draftDoc = 'lastDoc';{{end}}
|
||||||
H.load($writer, draftDoc, true);
|
var updatedStr = '{{.Post.Updated8601}}';
|
||||||
|
var updated = null;
|
||||||
|
if (updatedStr != '') {
|
||||||
|
updated = new Date(updatedStr);
|
||||||
|
}
|
||||||
|
var ok = H.load($writer, draftDoc, true, updated);
|
||||||
|
if (!ok) {
|
||||||
|
// Show "edited elsewhere" warning
|
||||||
|
$btnEraseEdit.el.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
var defaultTimeSet = false;
|
||||||
updateWordCount();
|
updateWordCount();
|
||||||
|
|
||||||
var typingTimer;
|
var typingTimer;
|
||||||
|
@ -190,6 +203,7 @@
|
||||||
data = JSON.parse(http.responseText);
|
data = JSON.parse(http.responseText);
|
||||||
id = data.data.id;
|
id = data.data.id;
|
||||||
nextURL = '{{if .SingleUser}}/d{{end}}/'+id;
|
nextURL = '{{if .SingleUser}}/d{{end}}/'+id;
|
||||||
|
localStorage.setItem('draft'+id+'-published', new Date().toISOString());
|
||||||
|
|
||||||
{{ if not .Post.Id }}
|
{{ if not .Post.Id }}
|
||||||
// Post created
|
// Post created
|
||||||
|
@ -258,6 +272,13 @@
|
||||||
publish(content, selectedFont);
|
publish(content, selectedFont);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
H.getEl('erase-edit').on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
H.remove(draftDoc);
|
||||||
|
H.remove(draftDoc+'-published');
|
||||||
|
justPublished = true; // Block auto-save
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
|
|
||||||
H.getEl('toggle-theme').on('click', function(e) {
|
H.getEl('toggle-theme').on('click', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -338,12 +359,20 @@
|
||||||
var doneTyping = function() {
|
var doneTyping = function() {
|
||||||
if (draftDoc == 'lastDoc' || $writer.el.value != origDoc) {
|
if (draftDoc == 'lastDoc' || $writer.el.value != origDoc) {
|
||||||
H.save($writer, draftDoc);
|
H.save($writer, draftDoc);
|
||||||
|
if (!defaultTimeSet) {
|
||||||
|
var lastLocalPublishStr = localStorage.getItem(draftDoc+'-published');
|
||||||
|
if (lastLocalPublishStr == null || lastLocalPublishStr == '') {
|
||||||
|
localStorage.setItem(draftDoc+'-published', updatedStr);
|
||||||
|
}
|
||||||
|
defaultTimeSet = true;
|
||||||
|
}
|
||||||
updateWordCount();
|
updateWordCount();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
window.addEventListener('beforeunload', function(e) {
|
window.addEventListener('beforeunload', function(e) {
|
||||||
if (draftDoc != 'lastDoc' && $writer.el.value == origDoc) {
|
if (draftDoc != 'lastDoc' && $writer.el.value == origDoc) {
|
||||||
H.remove(draftDoc);
|
H.remove(draftDoc);
|
||||||
|
H.remove(draftDoc+'-published');
|
||||||
} else if (!justPublished) {
|
} else if (!justPublished) {
|
||||||
doneTyping();
|
doneTyping();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue