This adds a "load more" button to the bottom of the draft posts page, which calls /api/me/posts with new parameters and the current page number. It then populates the page accordingly. Ref T696 - load anon. posts with ?anonymous=1&page=1 Ref T401 - completes UI for post loadingpull/356/head
@@ -16,6 +16,7 @@ import ( | |||
"html/template" | |||
"net/http" | |||
"regexp" | |||
"strconv" | |||
"strings" | |||
"sync" | |||
"time" | |||
@@ -691,6 +692,22 @@ func viewMyPostsAPI(app *App, u *User, w http.ResponseWriter, r *http.Request) e | |||
return ErrBadRequestedType | |||
} | |||
isAnonPosts := r.FormValue("anonymous") == "1" | |||
if isAnonPosts { | |||
pageStr := r.FormValue("page") | |||
pg, err := strconv.Atoi(pageStr) | |||
if err != nil { | |||
log.Error("Error parsing page parameter '%s': %s", pageStr, err) | |||
pg = 1 | |||
} | |||
p, err := app.db.GetAnonymousPosts(u, pg) | |||
if err != nil { | |||
return err | |||
} | |||
return impart.WriteSuccess(w, p, http.StatusOK) | |||
} | |||
var err error | |||
p := GetPostsCache(u.ID) | |||
if p == nil { | |||
@@ -287,6 +287,26 @@ func (h *Handler) UserAPI(f userHandlerFunc) http.HandlerFunc { | |||
return h.UserAll(false, f, apiAuth) | |||
} | |||
// UserWebAPI handles endpoints that accept a user authorized either via the web (cookies) or an Authorization header. | |||
func (h *Handler) UserWebAPI(f userHandlerFunc) http.HandlerFunc { | |||
return h.UserAll(false, f, func(app *App, r *http.Request) (*User, error) { | |||
// Authorize user via cookies | |||
u := getUserSession(app, r) | |||
if u != nil { | |||
return u, nil | |||
} | |||
// Fall back to access token, since user isn't logged in via web | |||
var err error | |||
u, err = apiAuth(app, r) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return u, nil | |||
}) | |||
} | |||
func (h *Handler) UserAll(web bool, f userHandlerFunc, a authFunc) http.HandlerFunc { | |||
return func(w http.ResponseWriter, r *http.Request) { | |||
handleFunc := func() error { | |||
@@ -109,7 +109,7 @@ func InitRoutes(apper Apper, r *mux.Router) *mux.Router { | |||
write.HandleFunc("/api/me", handler.All(viewMeAPI)).Methods("GET") | |||
apiMe := write.PathPrefix("/api/me/").Subrouter() | |||
apiMe.HandleFunc("/", handler.All(viewMeAPI)).Methods("GET") | |||
apiMe.HandleFunc("/posts", handler.UserAPI(viewMyPostsAPI)).Methods("GET") | |||
apiMe.HandleFunc("/posts", handler.UserWebAPI(viewMyPostsAPI)).Methods("GET") | |||
apiMe.HandleFunc("/collections", handler.UserAPI(viewMyCollectionsAPI)).Methods("GET") | |||
apiMe.HandleFunc("/password", handler.All(updatePassphrase)).Methods("POST") | |||
apiMe.HandleFunc("/self", handler.All(updateSettings)).Methods("POST") | |||
@@ -181,7 +181,7 @@ var localPosts = function() { | |||
undoDelete: UndoDelete, | |||
}; | |||
}(); | |||
var createPostEl = function(post) { | |||
var createPostEl = function(post, owned) { | |||
var $post = document.createElement('div'); | |||
var title = (post.title || post.id); | |||
title = title.replace(/</g, "<"); | |||
@@ -194,13 +194,21 @@ var createPostEl = function(post) { | |||
posted = getFormattedDate(new Date(post.created)) | |||
} | |||
var hasDraft = H.exists('draft' + post.id); | |||
$post.innerHTML += '<h4><date>' + posted + '</date> <a class="action" href="/pad/' + post.id + '">edit' + (hasDraft ? 'ed' : '') + '</a> <a class="delete action" href="/' + post.id + '" onclick="delPost(event, \'' + post.id + '\')">delete</a></h4>'; | |||
$post.innerHTML += '<h4><date>' + posted + '</date> <a class="action" href="/pad/' + post.id + '">edit' + (hasDraft ? 'ed' : '') + '</a> <a class="delete action" href="/' + post.id + '" onclick="delPost(event, \'' + post.id + '\'' + (owned === true ? ', true' : '') + ')">delete</a></h4>'; | |||
if (post.error) { | |||
$post.innerHTML += '<p class="error"><strong>Sync error:</strong> ' + post.error + ' <nav><a href="#" onclick="localPosts.dismissError(event, this)">dismiss</a> <a href="#" onclick="localPosts.deletePost(event, this, \''+post.id+'\')">remove post</a></nav></p>'; | |||
} | |||
if (post.summary) { | |||
$post.innerHTML += '<p>' + post.summary.replace(/</g, "<") + '</p>'; | |||
} else if (post.body) { | |||
var preview; | |||
if (post.body.length > 140) { | |||
preview = post.body.substr(0, 140) + '...'; | |||
} else { | |||
preview = post.body; | |||
} | |||
$post.innerHTML += '<p>' + preview.replace(/</g, "<") + '</p>'; | |||
} | |||
return $post; | |||
}; | |||
@@ -1,5 +1,11 @@ | |||
{{define "articles"}} | |||
{{template "header" .}} | |||
<style type="text/css"> | |||
a.loading { | |||
font-style: italic; | |||
color: #666; | |||
} | |||
</style> | |||
<div class="snug content-container"> | |||
@@ -15,7 +21,7 @@ | |||
{{ if .AnonymousPosts }} | |||
<p>These are your draft posts. You can share them individually (without a blog) or move them to your blog when you're ready.</p> | |||
<div class="atoms posts"> | |||
<div id="anon-posts" class="atoms posts"> | |||
{{ range $el := .AnonymousPosts }}<div id="post-{{.ID}}" class="post"> | |||
<h3><a href="/{{if $.SingleUser}}d/{{end}}{{.ID}}" itemprop="url">{{.DisplayTitle}}</a></h3> | |||
<h4> | |||
@@ -39,9 +45,12 @@ | |||
</h4> | |||
{{if .Summary}}<p>{{.SummaryHTML}}</p>{{end}} | |||
</div>{{end}} | |||
</div>{{ else }}<div id="no-posts-published"> | |||
</div> | |||
{{if eq (len .AnonymousPosts) 10}}<p id="load-more-p"><a href="#load">Load more...</a></p>{{end}} | |||
{{ else }}<div id="no-posts-published"> | |||
<p>Your anonymous and draft posts will show up here once you've published some. You'll be able to share them individually (without a blog) or move them to a blog when you're ready.</p> | |||
{{if not .SingleUser}}<p>Alternatively, see your blogs and their posts on your <a href="/me/c/">Blogs</a> page.</p>{{end}} | |||
<p class="text-cta"><a href="{{if .SingleUser}}/me/new{{else}}/{{end}}">Start writing</a></p></div>{{ end }} | |||
<div id="moving"></div> | |||
@@ -145,6 +154,50 @@ function postsLoaded(n) { | |||
syncing = true; | |||
}); | |||
} | |||
var $loadMore = H.getEl("load-more-p"); | |||
var curPage = 1; | |||
var isLoadingMore = false; | |||
function loadMorePosts() { | |||
if (isLoadingMore === true) { | |||
return; | |||
} | |||
var $link = this; | |||
isLoadingMore = true; | |||
$link.className = 'loading'; | |||
$link.textContent = 'Loading posts...'; | |||
var $posts = H.getEl("anon-posts"); | |||
curPage++; | |||
var http = new XMLHttpRequest(); | |||
var url = "/api/me/posts?anonymous=1&page=" + curPage; | |||
http.open("GET", url, true); | |||
http.setRequestHeader("Content-type", "application/json"); | |||
http.onreadystatechange = function() { | |||
if (http.readyState == 4) { | |||
if (http.status == 200) { | |||
var data = JSON.parse(http.responseText); | |||
for (var i=0; i<data.data.length; i++) { | |||
$posts.el.appendChild(createPostEl(data.data[i], true)); | |||
} | |||
if (data.data.length < 10) { | |||
$loadMore.el.parentNode.removeChild($loadMore.el); | |||
} | |||
} else { | |||
alert("Failed to load more posts. Please try again."); | |||
curPage--; | |||
} | |||
isLoadingMore = false; | |||
$link.className = ''; | |||
$link.textContent = 'Load more...'; | |||
} | |||
} | |||
http.send(); | |||
} | |||
$loadMore.el.querySelector('a').addEventListener('click', loadMorePosts); | |||
</script> | |||
<script src="/js/posts.js"></script> | |||